Quick overview

Current status

#library(nCov2019)
library(leaflet)
library(dplyr)
library(ggplot2)
library(plotly)
library(scales)
library(xts)
library(dygraphs)
library(corrplot)
library(lubridate)
library(fmsb)
library(forecast)
COVID<-read.csv("covid_19_data.csv")
COVID_2<-read.csv("COVID19_13-Apr.csv")

Format date:

Date<-as.Date(COVID_2$Date, format="%m/%d/%y") 

COVID_2$Date2<-Date
COVID_updated<-COVID_2 %>% filter(Date2==max(Date2))
leaflet(width = "100%") %>% 
  addProviderTiles("CartoDB.DarkMatter") %>% 
  setView(lng = 0, lat = 10, zoom = 1.5) %>% 
  addCircleMarkers(data = COVID_updated, 
                   lng = ~ Long,
                   lat = ~ Lat,
                   radius = ~ log(Confirmed+1),
                   color = rgb(218/255,65/255,56/255),
                   fillOpacity = ~ ifelse(Confirmed > 0, 1, 0),
                   stroke = FALSE,
                   label = ~ paste(Province.State,",",Country.Region, ": ", Confirmed)
                   )

Current top 10 countries:

COVID_top<-COVID_2 %>% filter(Date2==max(Date2)) %>% 
  group_by(Country.Region) %>% summarise(Total_confirmed=sum(Confirmed)) %>% 
  top_n(10,Total_confirmed) %>% arrange(desc(Total_confirmed))
plot<-ggplot(data=COVID_top
       , aes(x=Total_confirmed,y=reorder(Country.Region,Total_confirmed))) +
  geom_bar(stat ="identity",alpha=0.8,fill="firebrick3") +
  geom_text(aes(label=Total_confirmed), vjust=0.5, hjust=0.9,color="black", size=3.5) +
  scale_x_continuous(labels = comma) +
  labs(title = paste("Top 10 countries with confirmed cases as of ",max(COVID_2$Date2)),
       x = "Confirmed cases",
       y = "Country") +
  theme_minimal()

ggplotly(plot,tooltip = c("x"),width=750)

Time distribution:

COVID_2_Day<- COVID_2 %>% group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed),
                                                        World_deaths=sum(Deaths),
                                                        World_recovered=sum(Recovered))


COVID_Day_confirmed_series<-xts(COVID_2_Day$World_confirmed, order.by=COVID_2_Day$Date2)
COVID_Day_deaths_series<-xts(COVID_2_Day$World_deaths, order.by=COVID_2_Day$Date2)
COVID_Day_recovered_series<-xts(COVID_2_Day$World_recovered, order.by=COVID_2_Day$Date2)

Day_summary<-cbind(COVID_Day_confirmed_series,COVID_Day_deaths_series,COVID_Day_recovered_series)
dygraph(Day_summary, main = "SARS-COV2-outbreak: Total worldwide cases", 
        xlab="Date", ylab="Total cases",width = 750) %>% 
  dySeries("COVID_Day_confirmed_series", "Total cases",drawPoints = TRUE, 
           pointSize = 3, color=rgb(53/255,116/255,199/255)) %>% 
  dySeries("COVID_Day_deaths_series", "Total deaths",drawPoints = TRUE, 
           pointSize = 3, color=rgb(189/255,55/255,48/255)) %>% 
  dySeries("COVID_Day_recovered_series", "Total recovered",drawPoints = TRUE, 
           pointSize = 3, color=rgb(69/255,136/255,51/255)) %>% 
  dyRangeSelector()
New_count<-function(x)
{
  Daily_cases<-numeric(length(x))
  
  for(i in length(x):2)
  {
    Daily_cases[i]=x[i] - x[i-1]
  }
  return(Daily_cases)
}

New_cases<-New_count(COVID_2_Day$World_confirmed)
New_deaths<-New_count(COVID_2_Day$World_deaths)
New_recovered<-New_count(COVID_2_Day$World_recovered)
COVID_New_confirmed_series<-xts(New_cases, order.by=COVID_2_Day$Date2)
COVID_New_deaths_series<-xts(New_deaths, order.by=COVID_2_Day$Date2)
COVID_New_recovered_series<-xts(New_recovered, order.by=COVID_2_Day$Date2)

New_summary<-cbind(COVID_New_confirmed_series,COVID_New_deaths_series,COVID_New_recovered_series)
dygraph(New_summary, main = "SARS-COV2-outbreak: Total worldwide cases", 
        xlab="Date", ylab="Novel coronavirus cases",width = 750) %>% 
  dySeries("COVID_New_confirmed_series", "New cases",drawPoints = TRUE, 
           pointSize = 3, color=rgb(53/255,116/255,199/255)) %>% 
  dySeries("COVID_New_deaths_series", "New deaths",drawPoints = TRUE, 
           pointSize = 3, color=rgb(189/255,55/255,48/255)) %>% 
  dySeries("COVID_New_recovered_series", "New recovered",drawPoints = TRUE, 
           pointSize = 3, color=rgb(69/255,136/255,51/255)) %>% 
  dyRangeSelector()

Team members countries total cases:

COVID_2_Day_Lebanon<- COVID_2 %>% 
  filter(Country.Region %in% c("Lebanon")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Chile<- COVID_2 %>% 
  filter(Country.Region %in% c("Chile")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Colombia<- COVID_2 %>% 
  filter(Country.Region %in% c("Colombia")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_CostaRica<- COVID_2 %>% 
  filter(Country.Region %in% c("Costa Rica")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))


COVID_Day_series_Lebanon<-xts(COVID_2_Day_Lebanon$World_confirmed, order.by=COVID_2_Day_Lebanon$Date2)
COVID_Day_series_Chile<-xts(COVID_2_Day_Chile$World_confirmed, order.by=COVID_2_Day_Chile$Date2)
COVID_Day_series_Colombia<-xts(COVID_2_Day_Colombia$World_confirmed, order.by=COVID_2_Day_Colombia$Date2)
COVID_Day_series_CostaRica<-xts(COVID_2_Day_CostaRica$World_confirmed, order.by=COVID_2_Day_CostaRica$Date2)

Our_Countries<-cbind(COVID_Day_series_Lebanon,COVID_Day_series_Chile,COVID_Day_series_Colombia,COVID_Day_series_CostaRica)
dygraph(Our_Countries, main = "SARS-COV2-outbreak: Total cases by country", xlab="Date", ylab="Total cases",width = 750) %>% 
  dySeries("COVID_Day_series_Lebanon", "Lebanon",drawPoints = TRUE, 
           pointSize = 3, color=rgb(0,0,3/255)) %>% 
  dySeries("COVID_Day_series_Chile", "Chile",drawPoints = TRUE, 
           pointSize = 3,color=rgb(120/255,28/255,109/255)) %>% 
  dySeries("COVID_Day_series_Colombia", "Colombia",drawPoints = TRUE, 
           pointSize = 3,color=rgb(237/255,105/255,37/255)) %>% 
  dySeries("COVID_Day_series_CostaRica", "Costa Rica",drawPoints = TRUE,
           pointSize = 3,color=rgb(204/255,197/255,126/255)) %>% 
  dyRangeSelector()
New_Lebanon<-New_count(COVID_2_Day_Lebanon$World_confirmed)
New_Chile<-New_count(COVID_2_Day_Chile$World_confirmed)
New_Colombia<-New_count(COVID_2_Day_Colombia$World_confirmed)
New_CostaRica<-New_count(COVID_2_Day_CostaRica$World_confirmed)

COVID_New_series_Lebanon<-xts(New_Lebanon, order.by=COVID_2_Day_Lebanon$Date2)
COVID_New_series_Chile<-xts(New_Chile, order.by=COVID_2_Day_Chile$Date2)
COVID_New_series_Colombia<-xts(New_Colombia, order.by=COVID_2_Day_Colombia$Date2)
COVID_New_series_CostaRica<-xts(New_CostaRica, order.by=COVID_2_Day_CostaRica$Date2)

Our_New_Countries<-cbind(COVID_New_series_Lebanon,COVID_New_series_Chile,COVID_New_series_Colombia,COVID_New_series_CostaRica)
dygraph(Our_New_Countries, main = "SARS-COV2-outbreak: New cases by country", xlab="Date", ylab="Total cases",width = 750) %>% 
  dySeries("COVID_New_series_Lebanon", "Lebanon",drawPoints = TRUE, 
           pointSize = 3, color=rgb(0,0,3/255)) %>% 
  dySeries("COVID_New_series_Chile", "Chile",drawPoints = TRUE, 
           pointSize = 3,color=rgb(120/255,28/255,109/255)) %>% 
  dySeries("COVID_New_series_Colombia", "Colombia",drawPoints = TRUE, 
           pointSize = 3,color=rgb(237/255,105/255,37/255)) %>% 
  dySeries("COVID_New_series_CostaRica", "Costa Rica",drawPoints = TRUE,
           pointSize = 3,color=rgb(204/255,197/255,126/255)) %>% 
  dyRangeSelector()

Looking for correlations

fig <- plot_ly(COVID_updated, x = ~Confirmed, y = ~Deaths, z = ~Recovered, width=750) %>% 
  add_markers(text= ~Country.Region ,hoverinfo= "text",
              marker = list(color=rgb(189/255,55/255,48/255))) %>% 
  layout(title="Confirmed cases Vs. Deaths Vs. Recovered", scene = list(
                    xaxis = list(title = 'Confirmed'),
                     yaxis = list(title = 'Deaths'),
                     zaxis = list(title = 'Recovered'))) 
fig

For the number of cases

Human Development Index

HDI<-read.csv("Human Development Index (HDI)_2.csv",sep=";",dec=",")
COVID_Country<-COVID_2 %>% filter(Date2==max(Date2)) %>% 
  group_by(Country.Region) %>% summarise(Total_confirmed=sum(Confirmed),
                                         Total_deaths=sum(Deaths),
                                         Total_Recovered=sum(Recovered))

Remove after parentheses:

HDI$Country_2<-gsub("\\s*\\([^\\)]+\\)","",as.character(HDI$Country))
HDI$Country_2[HDI$Country_2=="United States"]<-"US"
HDI$Country_2[HDI$Country_2=="Korea"]<-"South Korea"

Population:

Population<-read.csv("World_population.csv",sep=";",dec=",")

Remove after commma:

Population$Country_Name_2<-gsub(",.*", "", as.character(Population$Country_Name))
Population$Country_Name_2[Population$Country_Name_2=="United States"]<-"US"
Population$Country_Name_2[Population$Country_Code=="KOR"]<-"South Korea"
Population$Country_Name_2[Population$Country_Code=="CZE"]<-"Czechia"

Natural Join:

COVID_3<- COVID_Country %>% inner_join(HDI,by=c("Country.Region"="Country_2")) %>% 
  inner_join(Population,by=c("Country.Region"="Country_Name_2")) %>% 
  select(Country.Region,Total_confirmed,Total_deaths,Total_Recovered,HDI_Rank_2018,Year_2018,
         Country_Code,Population_2018) %>% 
  mutate(Cases_million=(Total_confirmed/Population_2018)*1000000,
         Recovered_percentage=(Total_Recovered/Total_confirmed)*100)  

COVID_3<-COVID_3[!is.na(COVID_3$Population_2018),]

Countries with top 10 cases per million inhabitants

COVID3_top<-COVID_3 %>% top_n(10,Cases_million) %>% arrange(desc(Cases_million)) %>% 
  mutate(Cases_million=round(Cases_million,0))
plot<-ggplot(data=COVID3_top
       , aes(x=Cases_million,y=reorder(Country.Region,Cases_million))) +
  geom_bar(stat ="identity",alpha=0.8,fill="sky blue", ) +
  geom_text(aes(label=Cases_million), vjust=0.5, hjust=0.9,color="black", size=3.5) +
  scale_x_continuous(labels = comma) +
  labs(title = paste("Top 10 countries with confirmed cases per million habitant \n as of ",max(COVID_2$Date2)),
       x = "Confirmed cases per million habitants",
       y = "Country") +
  theme_minimal()

ggplotly(plot,tooltip = c("x"),width=750)

Plot the Human Development Index(HDI) Vs. the number of cases (applying a log transformation), and the proportion of recovered cases:

plot<-ggplot(data=COVID_3,aes(x=log(Cases_million),y=Year_2018,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(237/255,105/255,37/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="HDI Vs. logarithmus of COVID-19 cases by million inhabitants \n and proportion of recovered",
       x="ln(Cases/1M population)",
       y="HDI")

ggplotly(plot,tooltip = c("text"),width=750)
COVID_numeric_1<-COVID_3 %>% mutate(Log_cases=log(Cases_million),
                                    Death_percentage=(Total_deaths/Total_confirmed)*100) %>% 
  select(Log_cases,Recovered_percentage,Death_percentage,Year_2018)

corrplot(cor(COVID_numeric_1),method = "number",tl.col="black",tl.srt=15,
         col=colorRampPalette(c(rgb(204/255,197/255,126/255),rgb(237/255,105/255,37/255)))(200))

Measles<-read.csv("Measles_immunization.csv",sep=";",dec=".")
Measles$Country_2<-gsub("\\s*\\([^\\)]+\\)","",as.character(Measles$Country))
Measles$Country_2[Measles$Country_2=="United States"]<-"US"
Measles$Country_2[Measles$Country_2=="Korea"]<-"South Korea"
COVID_3<- COVID_3 %>% inner_join(Measles,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))

Health expenditure (% of GDP)

Health_expenditure<-read.csv("Health_expenditure_GDP.csv",sep=";",dec=".")
Health_expenditure$Country_2<-gsub("\\s*\\([^\\)]+\\)","",
                                   as.character(Health_expenditure$Country))
Health_expenditure$Country_2[Health_expenditure$Country_2=="United States"]<-"US"
Health_expenditure$Country_2[Health_expenditure$Country_2=="Korea"]<-"South Korea"
COVID_3<- COVID_3 %>% inner_join(Health_expenditure,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))
plot<-ggplot(data=COVID_3,aes(x=log(Cases_million),y=Expenditure_2016,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(204/255,197/255,126/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="Health expenditure (% of GDP) Vs. logarithmus of COVID-19 cases by million inhabitants \n and proportion of recovered",
       x="ln(Cases/1M population)",
       y="% of GDP in health")

ggplotly(plot,tooltip = c("text"),width=750)

Temperature

Temperature<-read.csv("GlobalLandTemperaturesByCountry.csv",sep=";")
Date_Temp<-as.Date(Temperature$dt, format="%d/%m/%Y") 

Temperature$Date_Temp<-Date_Temp

Extract month, filter IQ (Jan, Feb and Mar) and obtain the average IQ temperature:

Temperature<- Temperature %>% mutate(Month=month(Temperature$Date_Temp,label =TRUE),
                                     Year=year(Temperature$Date_Temp)) %>% 
  filter(Month %in% c("Jan","Feb","Mar") & Year>=2000)  %>% 
  group_by(Country) %>% summarise(Avg_IQ_temperature=mean(AverageTemperature))

Temperature<-Temperature[!is.na(Temperature$Avg_IQ_temperature),]
Temperature$Country_2<-gsub("\\s*\\([^\\)]+\\)","",
                                   as.character(Temperature$Country))
Temperature$Country_2[Temperature$Country_2=="United States"]<-"US"
Temperature$Country_2[Temperature$Country_2=="Korea"]<-"South Korea"
COVID_3<- COVID_3 %>% inner_join(Temperature,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))

#write.csv(COVID_3,"COVID_Covariables.csv")
plot<-ggplot(data=COVID_3,aes(x=log(Cases_million),y=Avg_IQ_temperature,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(120/255,28/255,109/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="Average IQ temperature Vs. logarithmus of COVID-19 cases by million inhabitants \n and proportion of recovered",
       x="ln(Cases/1M population)",
       y="Temperature (Celcius)")

ggplotly(plot,tooltip = c("text"),width=750)

For the number of deaths

###DTP immunization

DTP_immunization<-read.csv("Infants_lacking_immunization_DTP.csv",sep=";")

Remove after parentheses:

DTP_immunization$Country_2<-gsub("\\s*\\([^\\)]+\\)","",
                                 as.character(DTP_immunization$Country))
DTP_immunization$Country_2[DTP_immunization$Country_2=="United States"]<-"US"
DTP_immunization$Country_2[DTP_immunization$Country_2=="Korea"]<-"South Korea"
COVID_4<- COVID_Country %>% inner_join(DTP_immunization,
                                       by=c("Country.Region"="Country_2")) %>% 
  select(Country.Region,Total_confirmed,Total_deaths,Total_Recovered,
         Lack_DTP_inmmunization_2018) %>% 
  mutate(Recovered_percentage=(Total_Recovered/Total_confirmed)*100,
         Death_rate=(Total_deaths/Total_confirmed)*100)
plot<-ggplot(data=COVID_4,aes(x=Death_rate,y=Lack_DTP_inmmunization_2018,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(120/255,28/255,109/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="% infants lacking DTP immunization Vs. death rate and \n proportion of recovered",
       x="Novel coronavirus death rate",
       y="% of infants")

ggplotly(plot,tooltip = c("text"),width=750)

Infants lacking immunization, measles (% of one-year-olds)

COVID_4<- COVID_4 %>% inner_join(Measles,by=c("Country.Region"="Country_2")) %>% select(-c("Country"))
ggplot(COVID_4, aes(y=Measles_2018)) + 
  geom_boxplot(fill="dodgerblue4",outlier.shape = 21, 
               outlier.fill = "firebrick",alpha=0.75) +
  ggtitle("Boxplot of % infants lacking measles immunization") + ylab("% of infants") +
  theme_minimal()

ggplot(COVID_4, aes(Measles_2018)) + 
  geom_histogram(fill="dodgerblue4",bins=20,alpha=0.8) +
  ggtitle("Histogram of % infants lacking measles immunization") + 
  xlab("% of infants") + 
  ylab("Count") +
  theme_minimal()

plot<-ggplot(data=COVID_4,aes(x=Death_rate,y=Measles_2018,
                        size=Recovered_percentage,text=Country.Region)) +
  geom_point(color="black",fill=rgb(237/255,105/255,37/255),shape=21,alpha=0.6) +
  scale_size(range = c(3,15), name="Recovered \n percentage") +
  theme_minimal() + 
  theme(legend.position="bottom") +
  labs(title="% infants lacking measles immunization Vs. fatality rate \n and proportion of recovered",
       x="Fatality rate (%)",
       y="% of infants")

ggplotly(plot,tooltip = c("text"),width=750)

Fitting a regression model

Transforming with ln and converting temperature as kelvin:

Mod1<-lm(log(COVID_3$Cases_million)~log(COVID_3$Year_2018)+log(COVID_3$Measles_2018)+
           log(COVID_3$Expenditure_2016)+log(COVID_3$Avg_IQ_temperature+273.15))
summary(Mod1)

Call:
lm(formula = log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + 
    log(COVID_3$Measles_2018) + log(COVID_3$Expenditure_2016) + 
    log(COVID_3$Avg_IQ_temperature + 273.15))

Residuals:
    Min      1Q  Median      3Q     Max 
-4.0445 -0.8320 -0.1258  0.7860  5.0653 

Coefficients:
                                         Estimate Std. Error t value Pr(>|t|)    
(Intercept)                              32.48921   20.05878   1.620  0.10755    
log(COVID_3$Year_2018)                    7.70457    0.67547  11.406  < 2e-16 ***
log(COVID_3$Measles_2018)                 0.03906    0.12115   0.322  0.74762    
log(COVID_3$Expenditure_2016)             0.94790    0.31777   2.983  0.00337 ** 
log(COVID_3$Avg_IQ_temperature + 273.15) -4.85499    3.53196  -1.375  0.17146    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.389 on 140 degrees of freedom
Multiple R-squared:  0.6975,    Adjusted R-squared:  0.6889 
F-statistic:  80.7 on 4 and 140 DF,  p-value: < 2.2e-16

Stepwise with AIC critertion:

Mod2<-step(Mod1,direction = "both")
Start:  AIC=100.11
log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + log(COVID_3$Measles_2018) + 
    log(COVID_3$Expenditure_2016) + log(COVID_3$Avg_IQ_temperature + 
    273.15)

                                           Df Sum of Sq    RSS     AIC
- log(COVID_3$Measles_2018)                 1     0.200 270.14  98.222
- log(COVID_3$Avg_IQ_temperature + 273.15)  1     3.643 273.59 100.059
<none>                                                  269.94 100.115
- log(COVID_3$Expenditure_2016)             1    17.157 287.10 107.049
- log(COVID_3$Year_2018)                    1   250.858 520.80 193.402

Step:  AIC=98.22
log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + log(COVID_3$Expenditure_2016) + 
    log(COVID_3$Avg_IQ_temperature + 273.15)

                                           Df Sum of Sq    RSS     AIC
- log(COVID_3$Avg_IQ_temperature + 273.15)  1     3.567 273.71  98.124
<none>                                                  270.14  98.222
+ log(COVID_3$Measles_2018)                 1     0.200 269.94 100.115
- log(COVID_3$Expenditure_2016)             1    17.513 287.66 105.330
- log(COVID_3$Year_2018)                    1   306.428 576.57 206.153

Step:  AIC=98.12
log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + log(COVID_3$Expenditure_2016)

                                           Df Sum of Sq    RSS     AIC
<none>                                                  273.71  98.124
+ log(COVID_3$Avg_IQ_temperature + 273.15)  1      3.57 270.14  98.222
+ log(COVID_3$Measles_2018)                 1      0.12 273.59 100.059
- log(COVID_3$Expenditure_2016)             1     23.11 296.82 107.879
- log(COVID_3$Year_2018)                    1    445.08 718.79 236.122
summary(Mod2)

Call:
lm(formula = log(COVID_3$Cases_million) ~ log(COVID_3$Year_2018) + 
    log(COVID_3$Expenditure_2016))

Residuals:
    Min      1Q  Median      3Q     Max 
-3.9696 -0.7793  0.0030  0.8599  5.0838 

Coefficients:
                              Estimate Std. Error t value Pr(>|t|)    
(Intercept)                     4.9541     0.6584   7.525 5.53e-12 ***
log(COVID_3$Year_2018)          8.0064     0.5269  15.196  < 2e-16 ***
log(COVID_3$Expenditure_2016)   1.0627     0.3069   3.463 0.000707 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.388 on 142 degrees of freedom
Multiple R-squared:  0.6933,    Adjusted R-squared:  0.689 
F-statistic: 160.5 on 2 and 142 DF,  p-value: < 2.2e-16

Normality of residuals:

hist(Mod2$residuals)

shapiro.test(Mod2$residuals)

    Shapiro-Wilk normality test

data:  Mod2$residuals
W = 0.9852, p-value = 0.1221

Prediction power: separate between training and testing:

set.seed(179819)
Sample <- sample(1:length(COVID_3$Cases_million),length(COVID_3$Cases_million)*0.20)
t.testing<- COVID_3[Sample,]
t.training<- COVID_3[-Sample,]

Transform the training and testing variables as before:

t.training<-t.training %>% mutate(Cases_million_log=log(Cases_million),HDI_log=log(Year_2018),
                      GDP_log=log(Expenditure_2016),
                      Temperature_log_kelvin=log(Avg_IQ_temperature+273.15)) 

t.training<-t.training[,14:17]

t.testing<-t.testing %>% mutate(Cases_million_log=log(Cases_million),HDI_log=log(Year_2018),
                      GDP_log=log(Expenditure_2016),
                      Temperature_log_kelvin=log(Avg_IQ_temperature+273.15)) 

t.testing<-t.testing[,14:17]

Fit the same model with training

Mod3<-lm(Cases_million_log~., data=t.training)
summary(Mod3)

Call:
lm(formula = Cases_million_log ~ ., data = t.training)

Residuals:
    Min      1Q  Median      3Q     Max 
-4.0072 -0.8207  0.0104  0.8562  5.0870 

Coefficients:
                       Estimate Std. Error t value Pr(>|t|)    
(Intercept)             41.7853    23.2798   1.795  0.07536 .  
HDI_log                  7.6095     0.6688  11.378  < 2e-16 ***
GDP_log                  0.9415     0.3551   2.651  0.00919 ** 
Temperature_log_kelvin  -6.4811     4.0903  -1.584  0.11590    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.4 on 112 degrees of freedom
Multiple R-squared:  0.7091,    Adjusted R-squared:  0.7013 
F-statistic: 91.01 on 3 and 112 DF,  p-value: < 2.2e-16

Error functions:

# Residual Sum of Square (RSS)
RSS<-function(Pred,Actual) {
  ss<-sum((Actual-Pred)^2)
  return(ss)
}

# Residual Standard Error (RSE)
RSE<-function(Pred,Real,NumPred) {
  N<-length(Real)-NumPred-1  
  ss<-sqrt((1/N)*RSS(Pred,Real))
  return(ss)
}
# Mean Squared Error 
MSE <- function(Pred,Actual) {
  N<-length(Actual)
  ss<-(1/N)*RSS(Pred,Actual)
  return(ss)
}

# Relative error
RelativeError<-function(Pred,Actual) {
  ss<-sum(abs(Actual-Pred))/sum(abs(Actual))
  return(ss)
}

Prediction:

Pred<-predict(Mod3,t.testing)

Errors:

RSS_Mod3<-RSS(Pred,t.testing$Cases_million_log)
RSE_Mod3<-RSE(Pred,t.testing$Cases_million_log,dim(t.testing)[2]-1)
MSE_Mod3<-MSE(Pred,t.testing$Cases_million_log)
RelativeError_Mod3<-RelativeError(Pred,t.testing$Cases_million_log)

Mod3_Errors<-c(RSS_Mod3,RSE_Mod3,MSE_Mod3,RelativeError_Mod3)

Now, a model without temperature:

t.training <- t.training %>% select(-Temperature_log_kelvin)
t.testing <- t.testing %>% select(-Temperature_log_kelvin)
Mod4<-lm(Cases_million_log~., data=t.training)
summary(Mod4)

Call:
lm(formula = Cases_million_log ~ ., data = t.training)

Residuals:
    Min      1Q  Median      3Q     Max 
-3.9361 -0.7526 -0.0133  0.8424  5.1368 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)   4.9167     0.7280   6.754 6.54e-10 ***
HDI_log       8.1355     0.5845  13.919  < 2e-16 ***
GDP_log       1.1227     0.3385   3.317  0.00122 ** 
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.41 on 113 degrees of freedom
Multiple R-squared:  0.7026,    Adjusted R-squared:  0.6973 
F-statistic: 133.5 on 2 and 113 DF,  p-value: < 2.2e-16

Prediction:

Pred<-predict(Mod4,t.testing)

Errors:

RSS_Mod4<-RSS(Pred,t.testing$Cases_million_log)
RSE_Mod4<-RSE(Pred,t.testing$Cases_million_log,dim(t.testing)[2]-1)
MSE_Mod4<-MSE(Pred,t.testing$Cases_million_log)
RelativeError_Mod4<-RelativeError(Pred,t.testing$Cases_million_log)

Mod4_Errors<-c(RSS_Mod4,RSE_Mod4,MSE_Mod4,RelativeError_Mod4)

Create a radarplot:

Errors<-rbind(Mod3_Errors,Mod4_Errors)

rownames(Errors)<-c("Model with temperature","Model without temperature")

colnames(Errors)<-c("Residual Sum of Square","Residual Standard Error","Mean Squared Error","Relative error")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("With temperature","Without temperature"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Forecast by country

Republic of Costa Rica

Model with temperature

CostaRica_Temp<-read.csv("CostaRica_Temperature.csv",sep=";")
CostaRica_Temp$Date<-as.Date(CostaRica_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_CostaRica_Temp<-COVID_2_Day_CostaRica %>%
  inner_join(CostaRica_Temp,by=c("Date2"="Date"))

COVID_Day_series_CostaRica_Temp<-xts(COVID_2_Day_CostaRica_Temp$World_confirmed, order.by=COVID_2_Day_CostaRica_Temp$Date2)
COVID_series_CostaRica_Temp_Train<-COVID_Day_series_CostaRica_Temp[1:64] #Until March 25th
COVID_series_CostaRica_Temp_Test<-COVID_Day_series_CostaRica_Temp[65:length(COVID_Day_series_CostaRica_Temp)] #From March 26th onwards

Get the lagged difference:

plot(diff(COVID_series_CostaRica_Temp_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_CostaRica_Temp_Train))

Arima model: ARIMA(1,2,1)

#Auto Arima for Costa Rica:

ARIMA1_CostaRica<-auto.arima(COVID_series_CostaRica_Temp_Train,
                             xreg = COVID_2_Day_CostaRica_Temp$Temperature_CostaRica[1:64])
summary(ARIMA1_CostaRica)
Series: COVID_series_CostaRica_Temp_Train 
Regression with ARIMA(1,2,1) errors 

Coefficients:
          ar1     ma1     xreg
      -0.7967  0.4328  -0.2134
s.e.   0.1462  0.1840   0.1159

sigma^2 estimated as 7.227:  log likelihood=-147.93
AIC=303.86   AICc=304.57   BIC=312.37

Training set error measures:
                    ME     RMSE      MAE MPE MAPE      MASE         ACF1
Training set 0.4281342 2.581091 1.397242 NaN  Inf 0.4379416 -0.009839234

Predictive error functions:

#Relative error
RE <- function(Fore,Actual) {
  return(sum(abs(Fore-Actual))/abs(sum(Actual)))
}


#MAPE
MAPE<-function(Fore,Actual){
  return(
    mean(abs(Actual-Fore)/abs(Actual))*100
    )
}

# mean squared error (MSE)
MSE<-function(Fore,Actual) {
  N<-length(Actual)
  ss<-sum((Actual-Fore)^2)
  return((1/N)*ss)
}

#PFA
PFA <- function(Fore,Actual) {
  Total<-0
  N<-length(Fore)
  for(i in 1:N) {
    if(Fore[i]>=Actual[i])
      Total<-Total+1      
  }
  return(Total/N)
}

Forecast prediction to compare

Test_CostaRica_Temp<-COVID_2_Day_CostaRica_Temp$Temperature_CostaRica[65:length(COVID_Day_series_CostaRica_Temp)]


P_CRI_1<-forecast(ARIMA1_CostaRica,xreg = Test_CostaRica_Temp)

Calculate errors:

RE_CRI_1<-RE(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MAPE_CRI_1<-MAPE(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MSE_CRI_1<-MSE(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))
PFA_CRI_1<-PFA(P_CRI_1$mean,as.vector(COVID_series_CostaRica_Temp_Test))

Errors.CRI_1<-c(RE_CRI_1,MAPE_CRI_1,MSE_CRI_1,PFA_CRI_1)
Errors.CRI_1
[1]   0.02889533   3.30422945 196.94131428   0.42105263

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_CostaRica_Temp_Test),
               Fore=P_CRI_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 3.304229

Model without temperature

ARIMA(1,2,0)

#Auto Arima for Costa Rica:

ARIMA2_CostaRica<-auto.arima(COVID_series_CostaRica_Temp_Train)
summary(ARIMA2_CostaRica)
Series: COVID_series_CostaRica_Temp_Train 
ARIMA(1,2,0) 

Coefficients:
          ar1
      -0.4701
s.e.   0.1133

sigma^2 estimated as 7.607:  log likelihood=-150.5
AIC=304.99   AICc=305.19   BIC=309.24

Training set error measures:
                    ME     RMSE      MAE     MPE     MAPE      MASE       ACF1
Training set 0.5145597 2.692615 1.200452 7.86228 21.76863 0.3762612 0.04160954

Forecast prediction to compare

P_CRI_2<-forecast(ARIMA2_CostaRica,h=length(COVID_series_CostaRica_Temp_Test))

Calculate errors:

RE_CRI_2<-RE(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MAPE_CRI_2<-MAPE(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))
MSE_CRI_2<-MSE(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))
PFA_CRI_2<-PFA(P_CRI_2$mean,as.vector(COVID_series_CostaRica_Temp_Test))

Errors.CRI_2<-c(RE_CRI_2,MAPE_CRI_2,MSE_CRI_2,PFA_CRI_2)
Errors.CRI_2
[1]   0.02759212   3.26239839 190.14695712   0.36842105
Errors<-rbind(Errors.CRI_1,Errors.CRI_2)

rownames(Errors)<-c("ARIMAX(1,2,1)","ARIMA(1,2,0)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(1,2,1)","ARIMA(1,2,0)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model with temperature

Make model with the overall series

#Auto Arima for Costa Rica:

ARIMA_Final_CostaRica<-auto.arima(COVID_Day_series_CostaRica_Temp,
                             xreg = COVID_2_Day_CostaRica_Temp$Temperature_CostaRica)
summary(ARIMA_Final_CostaRica)
Series: COVID_Day_series_CostaRica_Temp 
Regression with ARIMA(1,1,1) errors 

Coefficients:
         ar1      ma1     xreg
      0.9775  -0.3795  -0.1737
s.e.  0.0216   0.1349   0.1794

sigma^2 estimated as 18.62:  log likelihood=-235.89
AIC=479.78   AICc=480.3   BIC=489.41

Training set error measures:
                    ME     RMSE      MAE MPE MAPE      MASE       ACF1
Training set 0.5865101 4.209981 2.336628 NaN  Inf 0.3130776 0.03301146

Final forecast:

Future_CostaRica_Temp<-CostaRica_Temp$Temperature_CostaRica[CostaRica_Temp$Date>max(COVID_2$Date2)]

P_CRI_Final<-forecast(ARIMA_Final_CostaRica,xreg = Future_CostaRica_Temp)
Low_lim_CRI<-data.frame(P_CRI_Final$lower)[,2]
Upp_lim_CRI<-data.frame(P_CRI_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_CostaRica_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_CostaRica_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_CostaRica_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_CRI <- xts(Low_lim_CRI,order.by=per_2)
Forecast_CRI <- xts(P_CRI_Final$mean,order.by=per_2)
Upp_lim_CRI <- xts(Upp_lim_CRI,order.by=per_2)
predNA <- rep(NA, length(Forecast_CRI))
B <- cbind(predNA, Low_lim_CRI, Forecast_CRI, Upp_lim_CRI)

all_series_CRI <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_CRI) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_CRI, main="SARS-COV2-outbreak: Total Costa Rica cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(189/255,44/255,47/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(10/255,44/255,119/255)) %>% 
  dyRangeSelector()

NA

Mexico

Model with temperature

Mexico_Temp<-read.csv("Mexico_Temperature.csv",sep=";")
Mexico_Temp$Date<-as.Date(Mexico_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Mexico<- COVID_2 %>% 
  filter(Country.Region %in% c("Mexico")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Mexico_Temp<-COVID_2_Day_Mexico %>%
  inner_join(Mexico_Temp,by=c("Date2"="Date"))

COVID_Day_series_Mexico_Temp<-xts(COVID_2_Day_Mexico_Temp$World_confirmed, order.by=COVID_2_Day_Mexico_Temp$Date2)
COVID_series_Mexico_Train<-COVID_Day_series_Mexico_Temp[1:74] #Until April 4th
COVID_series_Mexico_Test<-COVID_Day_series_Mexico_Temp[75:length(COVID_Day_series_Mexico_Temp)] #From April 5th onwards

Get the lagged difference:

plot(diff(COVID_series_Mexico_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_Mexico_Train))

Arima model: ARIMA(2,2,3)

#Auto Arima for Mexico:

#ARIMA1_Mexico<-auto.arima(COVID_series_Mexico_Train,
                             #xreg = COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Mexico)
Series: COVID_series_Mexico_Train 
Regression with ARIMA(2,2,3) errors 

Coefficients:
          ar1      ar2     ma1     ma2     ma3    xreg
      -0.9027  -0.7333  1.1212  0.8938  0.6734  0.0623
s.e.   0.1121   0.1592  0.1093  0.2027  0.1153  0.2567

sigma^2 estimated as 123.3:  log likelihood=-273.92
AIC=561.84   AICc=563.59   BIC=577.78

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE       ACF1
Training set 1.716467 10.48796 5.177678 NaN  Inf 0.2239162 -0.1047828

Forecast prediction to compare

Test_Mexico_Temp<-COVID_2_Day_Mexico_Temp$Temperature_Mexico[75:length(COVID_Day_series_Mexico_Temp)]


P_MEX_1<-forecast(ARIMA1_Mexico,xreg = Test_Mexico_Temp)

Calculate errors:

RE_MEX_1<-RE(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))
MAPE_MEX_1<-MAPE(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))
MSE_MEX_1<-MSE(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))
PFA_MEX_1<-PFA(P_MEX_1$mean,as.vector(COVID_series_Mexico_Test))

Errors.MEX_1<-c(RE_MEX_1,MAPE_MEX_1,MSE_MEX_1,PFA_MEX_1)
Errors.MEX_1
[1] 1.889868e-01 1.632085e+01 5.534272e+05 0.000000e+00

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_Mexico_Test),
               Fore=P_MEX_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 16.32085

Model without temperature

ARIMA(2,2,4)

#Auto Arima for Mexico:

ARIMA2_Mexico<-auto.arima(COVID_series_Mexico_Train)
summary(ARIMA2_Mexico)
Series: COVID_series_Mexico_Train 
ARIMA(2,2,4) 

Coefficients:
          ar1      ar2     ma1     ma2     ma3      ma4
      -0.7232  -0.7137  0.8661  0.6669  0.4019  -0.3260
s.e.   0.1415   0.1688  0.1697  0.2135  0.1529   0.1463

sigma^2 estimated as 116.5:  log likelihood=-272.23
AIC=558.45   AICc=560.2   BIC=574.39

Training set error measures:
                   ME     RMSE     MAE      MPE     MAPE      MASE        ACF1
Training set 2.187346 10.19436 4.88144 4.696633 18.33364 0.2111049 -0.05173888

Forecast prediction to compare

P_MEX_2<-forecast(ARIMA2_Mexico,h=length(COVID_series_Mexico_Test))

Calculate errors:

RE_MEX_2<-RE(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))
MAPE_MEX_2<-MAPE(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))
MSE_MEX_2<-MSE(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))
PFA_MEX_2<-PFA(P_MEX_2$mean,as.vector(COVID_series_Mexico_Test))

Errors.MEX_2<-c(RE_MEX_2,MAPE_MEX_2,MSE_MEX_2,PFA_MEX_2)
Errors.MEX_2
[1] 1.939056e-01 1.673569e+01 5.832504e+05 0.000000e+00
Errors<-rbind(Errors.MEX_1,Errors.MEX_2)

rownames(Errors)<-c("ARIMAX(2,2,3)","ARIMA(2,2,4)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(2,2,3)","ARIMA(2,2,4)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model with temperature

Make model with the overall series

#Auto Arima for Mexico:

ARIMA_Final_Mexico<-auto.arima(COVID_Day_series_Mexico_Temp,
                             xreg = COVID_2_Day_Mexico_Temp$Temperature_Mexico)
summary(ARIMA_Final_Mexico)
Series: COVID_Day_series_Mexico_Temp 
Regression with ARIMA(2,2,1) errors 

Coefficients:
         ar1     ar2      ma1     xreg
      0.4489  0.5325  -0.8817  -0.2137
s.e.  0.0992  0.0967   0.0531   0.9974

sigma^2 estimated as 618.9:  log likelihood=-373.91
AIC=757.82   AICc=758.62   BIC=769.79

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE       ACF1
Training set 3.719845 23.96071 10.93701 NaN  Inf 0.1924126 -0.0546056

Final forecast:

Future_Mexico_Temp<-Mexico_Temp$Temperature_Mexico[Mexico_Temp$Date>max(COVID_2$Date2)]

P_MEX_Final<-forecast(ARIMA_Final_Mexico,xreg = Future_Mexico_Temp)
Low_lim_MEX<-data.frame(P_MEX_Final$lower)[,2]
Upp_lim_MEX<-data.frame(P_MEX_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Mexico_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Mexico_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Mexico_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_MEX <- xts(Low_lim_MEX,order.by=per_2)
Forecast_MEX <- xts(P_MEX_Final$mean,order.by=per_2)
Upp_lim_MEX <- xts(Upp_lim_MEX,order.by=per_2)
predNA <- rep(NA, length(Forecast_MEX))
B <- cbind(predNA, Low_lim_MEX, Forecast_MEX, Upp_lim_MEX)

all_series_MEX <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_MEX) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_MEX, main="SARS-COV2-outbreak: Total Mexico cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(189/255,44/255,47/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(43/255,102/255,73/255)) %>% 
  dyRangeSelector()

NA

Italy

Model with temperature

Italy_Temp<-read.csv("Italy_Temperature.csv",sep=";")
Italy_Temp$Date<-as.Date(Italy_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Italy<- COVID_2 %>% 
  filter(Country.Region %in% c("Italy")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Italy_Temp<-COVID_2_Day_Italy %>%
  inner_join(Italy_Temp,by=c("Date2"="Date"))

COVID_Day_series_Italy_Temp<-xts(COVID_2_Day_Italy_Temp$World_confirmed, order.by=COVID_2_Day_Italy_Temp$Date2)
COVID_Day_series_Italy_Train<-COVID_Day_series_Italy_Temp[1:64] #Until March 25th
COVID_Day_series_Italy_Test<-COVID_Day_series_Italy_Temp[65:length(COVID_Day_series_Italy_Temp)] #From March 26th onwards

Get the lagged difference:

plot(diff(COVID_Day_series_Italy_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_Day_series_Italy_Train))

Arima model: ARIMA(1,2,0)

#Auto Arima for Italy:

ARIMA1_Italy<-auto.arima(COVID_Day_series_Italy_Train,
                             xreg = COVID_2_Day_Italy_Temp$Temperature_Italy[1:64])
#ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Italy)
Series: COVID_Day_series_Italy_Train 
Regression with ARIMA(1,2,0) errors 

Coefficients:
          ar1     xreg
      -0.5094  21.4065
s.e.   0.1119  20.2612

sigma^2 estimated as 489396:  log likelihood=-493.24
AIC=992.47   AICc=992.89   BIC=998.85

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE         ACF1
Training set 123.6808 677.3545 335.2238 NaN  Inf 0.2839123 -0.005148955

Forecast prediction to compare

Test_Italy_Temp<-COVID_2_Day_Italy_Temp$Temperature_Italy[65:length(COVID_Day_series_Italy_Temp)]


P_ITA_1<-forecast(ARIMA1_Italy,xreg = Test_Italy_Temp)

Calculate errors:

RE_ITA_1<-RE(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))
MAPE_ITA_1<-MAPE(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))
MSE_ITA_1<-MSE(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))
PFA_ITA_1<-PFA(P_ITA_1$mean,as.vector(COVID_Day_series_Italy_Test))

Errors.ITA_1<-c(RE_ITA_1,MAPE_ITA_1,MSE_ITA_1,PFA_ITA_1)
Errors.ITA_1
[1] 4.208045e-02 3.728108e+00 4.844373e+07 7.368421e-01

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_Day_series_Italy_Test),
               Fore=P_ITA_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 3.728108

Model without temperature

ARIMA(1,2,0)

#Auto Arima for Italy:
ARIMA2_Italy<-auto.arima(COVID_Day_series_Italy_Train)
summary(ARIMA2_Italy)
Series: COVID_Day_series_Italy_Train 
ARIMA(1,2,0) 

Coefficients:
          ar1
      -0.5414
s.e.   0.1046

sigma^2 estimated as 489700:  log likelihood=-493.79
AIC=991.58   AICc=991.79   BIC=995.84

Training set error measures:
                   ME     RMSE     MAE      MPE     MAPE      MASE        ACF1
Training set 125.8084 683.1877 295.442 5.335548 11.24576 0.2502197 -0.02348722

Forecast prediction to compare

P_ITA_2<-forecast(ARIMA2_Italy,h=length(COVID_Day_series_Italy_Test))

Calculate errors:

RE_ITA_2<-RE(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))
MAPE_ITA_2<-MAPE(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))
MSE_ITA_2<-MSE(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))
PFA_ITA_2<-PFA(P_ITA_2$mean,as.vector(COVID_Day_series_Italy_Test))

Errors.ITA_2<-c(RE_ITA_2,MAPE_ITA_2,MSE_ITA_2,PFA_ITA_2)
Errors.ITA_2
[1] 3.967952e-02 3.531253e+00 4.311796e+07 6.842105e-01
Errors<-rbind(Errors.ITA_1,Errors.ITA_2)

rownames(Errors)<-c("ARIMAX(1,2,0)","ARIMA(1,2,0)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(1,2,0)","ARIMA(1,2,0)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model without temperature

Make model with the overall series

#Auto Arima for Italy:

ARIMA_Final_Italy<-auto.arima(COVID_Day_series_Italy_Temp)
summary(ARIMA_Final_Italy)
Series: COVID_Day_series_Italy_Temp 
ARIMA(1,2,0) 

Coefficients:
          ar1
      -0.4579
s.e.   0.0987

sigma^2 estimated as 494348:  log likelihood=-645.54
AIC=1295.09   AICc=1295.24   BIC=1299.88

Training set error measures:
                   ME    RMSE      MAE     MPE     MAPE     MASE       ACF1
Training set 60.56058 690.275 354.0068 3.79996 8.394382 0.181979 0.00588171

Final forecast:

Future_Italy_Temp<-Italy_Temp$Temperature_Italy[Italy_Temp$Date>max(COVID_2$Date2)]

P_ITA_Final<-forecast(ARIMA_Final_Italy,h=length(Future_Italy_Temp))
Low_lim_ITA<-data.frame(P_ITA_Final$lower)[,2]
Upp_lim_ITA<-data.frame(P_ITA_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Italy_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Italy_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Italy_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_ITA <- xts(Low_lim_ITA,order.by=per_2)
Forecast_ITA <- xts(P_ITA_Final$mean,order.by=per_2)
Upp_lim_ITA <- xts(Upp_lim_ITA,order.by=per_2)
predNA <- rep(NA, length(Forecast_ITA))
B <- cbind(predNA, Low_lim_ITA, Forecast_ITA, Upp_lim_ITA)

all_series_ITA <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_ITA) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_ITA, main="SARS-COV2-outbreak: Total Italy cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(190/255,59/255,61/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(64/255,143/255,78/255)) %>% 
  dyRangeSelector()

NA

Lebanon

Model with temperature

Lebanon_Temp<-read.csv("Lebanon_Temperature.csv",sep=";")
Lebanon_Temp$Date<-as.Date(Lebanon_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Lebanon<- COVID_2 %>% 
  filter(Country.Region %in% c("Lebanon")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Lebanon_Temp<-COVID_2_Day_Lebanon %>%
  inner_join(Lebanon_Temp,by=c("Date2"="Date"))

COVID_Day_series_Lebanon_Temp<-xts(COVID_2_Day_Lebanon_Temp$World_confirmed, order.by=COVID_2_Day_Lebanon_Temp$Date2)
COVID_series_Lebanon_Train<-COVID_Day_series_Lebanon_Temp[1:74] #Until April 4th
COVID_series_Lebanon_Test<-COVID_Day_series_Lebanon_Temp[75:length(COVID_Day_series_Lebanon_Temp)] #From April 4th onwards

Get the lagged difference:

plot(diff(COVID_series_Lebanon_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_Lebanon_Train))

Arima model: ARIMA(1,1,2)

#Auto Arima for Lebanon:

ARIMA1_Lebanon<-auto.arima(COVID_series_Lebanon_Train,
                             xreg = COVID_2_Day_Lebanon_Temp$Temperature_Lebanon[1:74])
#ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Lebanon)
Series: COVID_series_Lebanon_Train 
Regression with ARIMA(1,1,2) errors 

Coefficients:
         ar1      ma1     ma2    xreg
      0.9216  -0.8504  0.4970  0.0461
s.e.  0.0488   0.0989  0.1263  0.2338

sigma^2 estimated as 60.69:  log likelihood=-252.36
AIC=514.72   AICc=515.62   BIC=526.17

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE        ACF1
Training set 1.101941 7.522299 3.444682 NaN  Inf 0.4835803 -0.03853757

Forecast prediction to compare

Test_Lebanon_Temp<-COVID_2_Day_Lebanon_Temp$Temperature_Lebanon[75:length(COVID_Day_series_Lebanon_Temp)]


P_LBN_1<-forecast(ARIMA1_Lebanon,xreg = Test_Lebanon_Temp)

Calculate errors:

RE_LBN_1<-RE(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))
MAPE_LBN_1<-MAPE(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))
MSE_LBN_1<-MSE(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))
PFA_LBN_1<-PFA(P_LBN_1$mean,as.vector(COVID_series_Lebanon_Test))

Errors.LBN_1<-c(RE_LBN_1,MAPE_LBN_1,MSE_LBN_1,PFA_LBN_1)
Errors.LBN_1
[1]   0.03207225   3.07734487 532.24875637   0.33333333

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_Lebanon_Test),
               Fore=P_LBN_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 3.077345

Model without temperature

ARIMA(0,2,2)

#Auto Arima for Lebanon:
ARIMA2_Lebanon<-auto.arima(COVID_series_Lebanon_Train)
summary(ARIMA2_Lebanon)
Series: COVID_series_Lebanon_Train 
ARIMA(0,2,2) 

Coefficients:
          ma1     ma2
      -0.8655  0.4654
s.e.   0.0939  0.1264

sigma^2 estimated as 61.55:  log likelihood=-249.92
AIC=505.84   AICc=506.19   BIC=512.67

Training set error measures:
                   ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
Training set 0.286841 7.630167 3.533028 2.334374 15.75454 0.4959827 -0.05944259

Forecast prediction to compare

P_LBN_2<-forecast(ARIMA2_Lebanon,h=length(COVID_series_Lebanon_Test))

Calculate errors:

RE_LBN_2<-RE(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))
MAPE_LBN_2<-MAPE(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))
MSE_LBN_2<-MSE(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))
PFA_LBN_2<-PFA(P_LBN_2$mean,as.vector(COVID_series_Lebanon_Test))

Errors.LBN_2<-c(RE_LBN_2,MAPE_LBN_2,MSE_LBN_2,PFA_LBN_2)
Errors.LBN_2
[1]  0.01150777  1.15133297 56.61087377  0.55555556
Errors<-rbind(Errors.LBN_1,Errors.LBN_2)

rownames(Errors)<-c("ARIMAX(1,1,2)","ARIMA(0,2,2)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(1,1,2)","ARIMA(0,2,2)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model without temperature

Make model with the overall series

#Auto Arima for Lebanon:

ARIMA_Final_Lebanon<-auto.arima(COVID_Day_series_Lebanon_Temp)
summary(ARIMA_Final_Lebanon)
Series: COVID_Day_series_Lebanon_Temp 
ARIMA(2,2,0) 

Coefficients:
          ar1      ar2
      -0.9016  -0.2596
s.e.   0.1070   0.1064

sigma^2 estimated as 62.97:  log likelihood=-282.13
AIC=570.25   AICc=570.57   BIC=577.44

Training set error measures:
                    ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
Training set 0.1748628 7.741674 3.821745 2.495435 12.97023 0.4958593 -0.02103357

Final forecast:

Future_Lebanon_Temp<-Lebanon_Temp$Temperature_Lebanon[Lebanon_Temp$Date>max(COVID_2$Date2)]

P_LBN_Final<-forecast(ARIMA_Final_Lebanon,h=length(Future_Lebanon_Temp))
Low_lim_LBN<-data.frame(P_LBN_Final$lower)[,2]
Upp_lim_LBN<-data.frame(P_LBN_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Lebanon_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Lebanon_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Lebanon_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_LBN <- xts(Low_lim_LBN,order.by=per_2)
Forecast_LBN <- xts(P_LBN_Final$mean,order.by=per_2)
Upp_lim_LBN <- xts(Upp_lim_LBN,order.by=per_2)
predNA <- rep(NA, length(Forecast_ITA))
B <- cbind(predNA, Low_lim_LBN, Forecast_LBN, Upp_lim_LBN)

all_series_LBN <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_LBN) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_LBN, main="SARS-COV2-outbreak: Total Lebanon cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(218/255,55/255,50/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(73/255,163/255,90/255)) %>% 
  dyRangeSelector()

NA

Colombia

Model with temperature

Colombia_Temp<-read.csv("Colombia_Temperature.csv",sep=";")
Colombia_Temp$Date<-as.Date(Colombia_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Colombia<- COVID_2 %>% 
  filter(Country.Region %in% c("Colombia")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Colombia_Temp<-COVID_2_Day_Colombia %>%
  inner_join(Colombia_Temp,by=c("Date2"="Date"))

COVID_Day_series_Colombia_Temp<-xts(COVID_2_Day_Colombia_Temp$World_confirmed, order.by=COVID_2_Day_Colombia_Temp$Date2)
COVID_series_Colombia_Train<-COVID_Day_series_Colombia_Temp[1:74] #Until April 4th
COVID_series_Colombia_Test<-COVID_Day_series_Colombia_Temp[75:length(COVID_Day_series_Colombia_Temp)] #From April 5th onwards

Get the lagged difference:

plot(diff(COVID_series_Colombia_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_Colombia_Train))

Arima model: ARIMA(2,2,2)

#Auto Arima for Colombia:

ARIMA1_Colombia<-auto.arima(COVID_series_Colombia_Train,
                             xreg = COVID_2_Day_Colombia_Temp$Temperature_Colombia[1:74])
#ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Colombia)
Series: COVID_series_Colombia_Train 
Regression with ARIMA(2,2,2) errors 

Coefficients:
          ar1      ar2      ma1     ma2     xreg
      -0.2675  -0.8894  -0.0039  0.6242  -0.2343
s.e.   0.1037   0.1016   0.1495  0.1732   0.5167

sigma^2 estimated as 217.7:  log likelihood=-294.03
AIC=600.06   AICc=601.35   BIC=613.72

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE        ACF1
Training set 2.197353 14.04005 6.409043 NaN  Inf 0.3327597 -0.07966735

Forecast prediction to compare

Test_Colombia_Temp<-COVID_2_Day_Colombia_Temp$Temperature_Colombia[75:length(COVID_Day_series_Colombia_Temp)]


P_COL_1<-forecast(ARIMA1_Colombia,xreg = Test_Colombia_Temp)

Calculate errors:

RE_COL_1<-RE(P_COL_1$mean,as.vector(COVID_series_Colombia_Test))
MAPE_COL_1<-MAPE(P_COL_1$mean,as.vector(COVID_series_Colombia_Test))
MSE_COL_1<-MSE(P_COL_1$mean,as.vector(COVID_series_Colombia_Test))
PFA_COL_1<-PFA(P_COL_1$mean,as.vector(COVID_series_Colombia_Test))

Errors.COL_1<-c(RE_COL_1,MAPE_COL_1,PFA_COL_1,MSE_COL_1)
Errors.COL_1
[1] 1.013233e-01 9.089187e+00 2.222222e-01 7.381917e+04

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_Colombia_Test),
               Fore=P_COL_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 9.089187

Model without temperature

ARIMA(2,2,2)

#Auto Arima for Colombia:
ARIMA2_Colombia<-auto.arima(COVID_series_Colombia_Train)
summary(ARIMA2_Colombia)
Series: COVID_series_Colombia_Train 
ARIMA(2,2,2) 

Coefficients:
          ar1      ar2     ma1     ma2
      -0.2672  -0.8913  0.0014  0.6244
s.e.   0.1037   0.1025  0.1486  0.1753

sigma^2 estimated as 215.1:  log likelihood=-294.13
AIC=598.26   AICc=599.17   BIC=609.65

Training set error measures:
                   ME     RMSE      MAE      MPE     MAPE      MASE        ACF1
Training set 2.199355 14.05992 6.130841 5.459672 19.97005 0.3183153 -0.08053967

Forecast prediction to compare

P_COL_2<-forecast(ARIMA2_Colombia,h=length(COVID_series_Colombia_Test))

Calculate errors:

RE_COL_2<-RE(P_COL_2$mean,as.vector(COVID_series_Colombia_Test))
MAPE_COL_2<-MAPE(P_COL_2$mean,as.vector(COVID_series_Colombia_Test))
MSE_COL_2<-MSE(P_COL_2$mean,as.vector(COVID_series_Colombia_Test))
PFA_COL_2<-PFA(P_COL_2$mean,as.vector(COVID_series_Colombia_Test))

Errors.COL_2<-c(RE_COL_2,MAPE_COL_2,MSE_COL_2,PFA_COL_2)
Errors.COL_2
[1] 1.008711e-01 9.052198e+00 7.314569e+04 2.222222e-01
Errors<-rbind(Errors.COL_1,Errors.COL_2)

rownames(Errors)<-c("ARIMAX(2,2,2)","ARIMA(2,2,2)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(2,2,2)","ARIMA(2,2,2)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model without temperature

Make model with the overall series

#Auto Arima for Colombia:

ARIMA_Final_Colombia<-auto.arima(COVID_Day_series_Colombia_Temp)
summary(ARIMA_Final_Colombia)
Series: COVID_Day_series_Colombia_Temp 
ARIMA(4,2,1) 

Coefficients:
         ar1      ar2     ar3      ar4      ma1
      0.5647  -0.4749  0.5634  -0.5138  -0.6402
s.e.  0.1456   0.1196  0.1304   0.1189   0.1227

sigma^2 estimated as 706.6:  log likelihood=-378.97
AIC=769.93   AICc=771.07   BIC=784.3

Training set error measures:
                   ME     RMSE      MAE     MPE     MAPE      MASE       ACF1
Training set 3.992104 25.43636 12.38659 5.38642 19.71782 0.3561361 -0.1491261

Final forecast:

Future_Colombia_Temp<-Colombia_Temp$Temperature_Colombia[Colombia_Temp$Date>max(COVID_2$Date2)]

P_COL_Final<-forecast(ARIMA_Final_Colombia,h=length(Future_Colombia_Temp))
Low_lim_COL<-data.frame(P_COL_Final$lower)[,2]
Upp_lim_COL<-data.frame(P_COL_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Colombia_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Colombia_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Colombia_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_COL <- xts(Low_lim_COL,order.by=per_2)
Forecast_COL <- xts(P_COL_Final$mean,order.by=per_2)
Upp_lim_COL <- xts(Upp_lim_COL,order.by=per_2)
predNA <- rep(NA, length(Forecast_COL))
B <- cbind(predNA, Low_lim_COL, Forecast_COL, Upp_lim_COL)

all_series_COL <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_COL) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_COL, main="SARS-COV2-outbreak: Total Colombia cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(17/255,57/255,141/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(246/255,209/255,75/255)) %>% 
  dyRangeSelector()

NA

Chile

Model with temperature

Chile_Temp<-read.csv("Chile_Temperature.csv",sep=";")
Chile_Temp$Date<-as.Date(Chile_Temp$Date,format="%d/%m/%Y")
COVID_2_Day_Chile<- COVID_2 %>% 
  filter(Country.Region %in% c("Chile")) %>% 
  group_by(Date2) %>% summarise(World_confirmed=sum(Confirmed))

COVID_2_Day_Chile_Temp<-COVID_2_Day_Chile %>%
  inner_join(Chile_Temp,by=c("Date2"="Date"))

COVID_Day_series_Chile_Temp<-xts(COVID_2_Day_Chile_Temp$World_confirmed, order.by=COVID_2_Day_Chile_Temp$Date2)
COVID_series_Chile_Train<-COVID_Day_series_Chile_Temp[1:76] #Until April 6th
COVID_series_Chile_Test<-COVID_Day_series_Chile_Temp[77:length(COVID_Day_series_Chile_Temp)] #From April 7th onwards

Get the lagged difference:

plot(diff(COVID_series_Chile_Train),type="l",main="1st ") 

Correlograms of first diference:

tsdisplay(diff(COVID_series_Chile_Train))

Arima model: ARIMA(1,1,0)

#Auto Arima for Chile:

ARIMA1_Chile<-auto.arima(COVID_series_Chile_Train,
                             xreg = COVID_2_Day_Chile_Temp$Temperature_Chile[1:76])
#ARIMA1_Mexico<-Arima(COVID_series_Mexico_Train,order=c(2,2,3),xreg=COVID_2_Day_Mexico_Temp$Temperature_Mexico[1:74])
summary(ARIMA1_Chile)
Series: COVID_series_Chile_Train 
Regression with ARIMA(1,1,0) errors 

Coefficients:
         ar1     xreg
      0.9734  -1.3786
s.e.  0.0265   1.2712

sigma^2 estimated as 1708:  log likelihood=-386
AIC=778.01   AICc=778.35   BIC=784.96

Training set error measures:
                   ME     RMSE      MAE MPE MAPE      MASE       ACF1
Training set 6.143615 40.50828 20.18216 NaN  Inf 0.3143638 -0.5175849

Forecast prediction to compare

Test_Chile_Temp<-COVID_2_Day_Chile_Temp$Temperature_Chile[77:length(COVID_Day_series_Chile_Temp)]


P_CHL_1<-forecast(ARIMA1_Chile,xreg = Test_Chile_Temp)

Calculate errors:

RE_CHL_1<-RE(P_CHL_1$mean,as.vector(COVID_series_Chile_Test))
MAPE_CHL_1<-MAPE(P_CHL_1$mean,as.vector(COVID_series_Chile_Test))
MSE_CHL_1<-MSE(P_CHL_1$mean,as.vector(COVID_series_Chile_Test))
PFA_CHL_1<-PFA(P_CHL_1$mean,as.vector(COVID_series_Chile_Test))

Errors.CHL_1<-c(RE_CHL_1,MAPE_CHL_1,MSE_CHL_1,PFA_CHL_1)
Errors.CHL_1
[1] 4.702935e-02 4.379044e+00 1.290917e+05 1.428571e-01

Manual MAPE:

dd<-data.frame(Actual=as.vector(COVID_series_Chile_Test),
               Fore=P_CHL_1$mean
)
dd<-dd %>% mutate(Abs_Per_Error=abs(Actual-Fore)/abs(Actual))
mean(dd$Abs_Per_Error)*100
[1] 4.379044

Model without temperature

ARIMA(1,2,4)

#Auto Arima for Colombia:
ARIMA2_Chile<-auto.arima(COVID_series_Chile_Train)
summary(ARIMA2_Chile)
Series: COVID_series_Chile_Train 
ARIMA(1,2,4) 

Coefficients:
         ar1      ma1     ma2     ma3      ma4
      0.8988  -1.8812  1.0871  0.0198  -0.0671
s.e.  0.0654   0.1487  0.2748  0.2611   0.1410

sigma^2 estimated as 843.5:  log likelihood=-354.47
AIC=720.93   AICc=722.19   BIC=734.76

Training set error measures:
                   ME     RMSE      MAE      MPE    MAPE     MASE        ACF1
Training set 2.726442 27.67343 12.61158 8.098567 15.1208 0.196442 -0.02684075

Forecast prediction to compare

P_CHL_2<-forecast(ARIMA2_Chile,h=length(COVID_series_Chile_Test))

Calculate errors:

RE_CHL_2<-RE(P_CHL_2$mean,as.vector(COVID_series_Chile_Test))
MAPE_CHL_2<-MAPE(P_CHL_2$mean,as.vector(COVID_series_Chile_Test))
MSE_CHL_2<-MSE(P_CHL_2$mean,as.vector(COVID_series_Chile_Test))
PFA_CHL_2<-PFA(P_CHL_2$mean,as.vector(COVID_series_Chile_Test))

Errors.CHL_2<-c(RE_CHL_2,MAPE_CHL_2,MSE_CHL_2,PFA_CHL_2)
Errors.CHL_2
[1] 2.831325e-02 2.665247e+00 4.677021e+04 1.428571e-01
Errors<-rbind(Errors.CHL_1,Errors.CHL_2)

rownames(Errors)<-c("ARIMAX(1,1,0)","ARIMA(1,2,4)")

colnames(Errors)<-c("Relative error","Mean Abs % error","Mean Squared Error","PFA")

Errors<-as.data.frame(Errors)

maximum<-apply(Errors,2,max)

minimum<-apply(Errors,2,min)

Errors<-rbind(minimum,Errors)

Errors<-rbind(maximum,Errors)
radarchart(Errors,maxmin=TRUE,axistype=4,axislabcol="slategray4",
           centerzero=FALSE,seg=8,cglcol="gray67",
           pcol=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"),
           plty=1,
           plwd=3,
           title="Error comparison")

legend <-legend(1.5,1, legend=c("ARIMAX(1,1,0)","ARIMA(1,2,4)"),
                 seg.len=-1.4,
                 title="Errors",
                 pch=21, 
                 bty="n" ,lwd=3, y.intersp=1, horiz=FALSE,
                 col=c("dodgerblue2","firebrick2","darkorange2","darkorchid2"))

Conclusion: Keep model without temperature

Make model with the overall series

#Auto Arima for Chile:

ARIMA_Final_Chile<-auto.arima(COVID_Day_series_Chile_Temp)
summary(ARIMA_Final_Chile)
Series: COVID_Day_series_Chile_Temp 
ARIMA(0,2,1) 

Coefficients:
          ma1
      -0.4303
s.e.   0.1014

sigma^2 estimated as 1960:  log likelihood=-421.54
AIC=847.08   AICc=847.24   BIC=851.87

Training set error measures:
                   ME   RMSE      MAE      MPE    MAPE     MASE       ACF1
Training set 6.995765 43.459 20.42083 5.151059 16.0157 0.222526 -0.0332737

Final forecast:

Future_Chile_Temp<-Chile_Temp$Temperature_Chile[Chile_Temp$Date>max(COVID_2$Date2)]

P_CHL_Final<-forecast(ARIMA_Final_Chile,h=length(Future_Chile_Temp))
Low_lim_CHL<-data.frame(P_CHL_Final$lower)[,2]
Upp_lim_CHL<-data.frame(P_CHL_Final$upper)[,2]

For making the plot:

##Data periods
per_1 <- as.Date(as.character(COVID_2_Day_Chile_Temp$Date2))
per_2 <- seq(as.Date(max(COVID_2_Day_Chile_Temp$Date2)+1,format="%Y-%m-%d"), as.Date("2020-04-30",format="%Y-%m-%d"),"1 day")


# Merge forecast + actuals
data <- xts(COVID_Day_series_Chile_Temp,order.by=per_1) 
dataNA <- rep(NA, length(data))
A <- cbind(data,dataNA,dataNA,dataNA)


Low_lim_CHL <- xts(Low_lim_CHL,order.by=per_2)
Forecast_CHL <- xts(P_CHL_Final$mean,order.by=per_2)
Upp_lim_CHL <- xts(Upp_lim_CHL,order.by=per_2)
predNA <- rep(NA, length(Forecast_CHL))
B <- cbind(predNA, Low_lim_CHL, Forecast_CHL, Upp_lim_CHL)

all_series_CHL <- data.frame(rbind(as.matrix(A),as.matrix(B)))
colnames(all_series_CHL) <- c('Actual', 'Lower_limit', 'Forecast', 'Upper_limit')
dygraph(all_series_CHL, main="SARS-COV2-outbreak: Total Chile cases",xlab="Date", ylab="Novel coronavirus cases",width = 750)%>%
  dySeries(c('Lower_limit', 'Forecast', 'Upper_limit'),label="Forecast",strokeWidth=2,
           drawPoints = TRUE, pointSize = 2, color=rgb(196/255,60/255,44/255)) %>%
  dySeries("Actual",drawPoints = TRUE, strokeWidth=2, pointSize = 2,
           color=rgb(16/255,59/255,160/255)) %>% 
  dyRangeSelector()

NA
#Forecasts<-list(all_series_CRI,all_series_MEX,all_series_ITA,all_series_LBN,all_series_COL,
                #all_series_CHL)
#names(Forecasts)<-c("Costa Rica","Mexico","Italy","Lebanon","Colombia","Chile")
#Forecasts$Mexico

#save(Forecasts, file="Forecasts.RData")
LS0tCnRpdGxlOiAiQ09WSUQtMTkgT3V0YnJlYWs6IFdvcmxkd2lkZSBhbmFseXNpcyIKYXV0aG9yOiAiQW91biwgQ2FtYXJnbywgTWFydGluZXosIFJvZHJpZ3VleiIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiB0cnVlCiAgICAgIHNtb290aF9zY3JvbGw6IHRydWUKICAgIHRoZW1lOiBjb3NtbwogICAgIAotLS0KIVtdKENvcm9uYXZpcnVzLmpwZykKCiMgUXVpY2sgb3ZlcnZpZXcKCiMjIEN1cnJlbnQgc3RhdHVzCgoKYGBge3IsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQojbGlicmFyeShuQ292MjAxOSkKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KHNjYWxlcykKbGlicmFyeSh4dHMpCmxpYnJhcnkoZHlncmFwaHMpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGZtc2IpCmxpYnJhcnkoZm9yZWNhc3QpCmBgYAoKYGBge3J9CkNPVklEPC1yZWFkLmNzdigiY292aWRfMTlfZGF0YS5jc3YiKQpDT1ZJRF8yPC1yZWFkLmNzdigiQ09WSUQxOV8xMy1BcHIuY3N2IikKYGBgCgpGb3JtYXQgZGF0ZToKYGBge3J9CkRhdGU8LWFzLkRhdGUoQ09WSURfMiREYXRlLCBmb3JtYXQ9IiVtLyVkLyV5IikgCgpDT1ZJRF8yJERhdGUyPC1EYXRlCmBgYAoKYGBge3J9CkNPVklEX3VwZGF0ZWQ8LUNPVklEXzIgJT4lIGZpbHRlcihEYXRlMj09bWF4KERhdGUyKSkKYGBgCgpgYGB7cix3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CmxlYWZsZXQod2lkdGggPSAiMTAwJSIpICU+JSAKICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCLkRhcmtNYXR0ZXIiKSAlPiUgCiAgc2V0VmlldyhsbmcgPSAwLCBsYXQgPSAxMCwgem9vbSA9IDEuNSkgJT4lIAogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IENPVklEX3VwZGF0ZWQsIAogICAgICAgICAgICAgICAgICAgbG5nID0gfiBMb25nLAogICAgICAgICAgICAgICAgICAgbGF0ID0gfiBMYXQsCiAgICAgICAgICAgICAgICAgICByYWRpdXMgPSB+IGxvZyhDb25maXJtZWQrMSksCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IHJnYigyMTgvMjU1LDY1LzI1NSw1Ni8yNTUpLAogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSB+IGlmZWxzZShDb25maXJtZWQgPiAwLCAxLCAwKSwKICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgbGFiZWwgPSB+IHBhc3RlKFByb3ZpbmNlLlN0YXRlLCIsIixDb3VudHJ5LlJlZ2lvbiwgIjogIiwgQ29uZmlybWVkKQogICAgICAgICAgICAgICAgICAgKQpgYGAKCkN1cnJlbnQgdG9wIDEwIGNvdW50cmllczoKYGBge3J9CkNPVklEX3RvcDwtQ09WSURfMiAlPiUgZmlsdGVyKERhdGUyPT1tYXgoRGF0ZTIpKSAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeS5SZWdpb24pICU+JSBzdW1tYXJpc2UoVG90YWxfY29uZmlybWVkPXN1bShDb25maXJtZWQpKSAlPiUgCiAgdG9wX24oMTAsVG90YWxfY29uZmlybWVkKSAlPiUgYXJyYW5nZShkZXNjKFRvdGFsX2NvbmZpcm1lZCkpCmBgYAoKYGBge3J9CnBsb3Q8LWdncGxvdChkYXRhPUNPVklEX3RvcAogICAgICAgLCBhZXMoeD1Ub3RhbF9jb25maXJtZWQseT1yZW9yZGVyKENvdW50cnkuUmVnaW9uLFRvdGFsX2NvbmZpcm1lZCkpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ImlkZW50aXR5IixhbHBoYT0wLjgsZmlsbD0iZmlyZWJyaWNrMyIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPVRvdGFsX2NvbmZpcm1lZCksIHZqdXN0PTAuNSwgaGp1c3Q9MC45LGNvbG9yPSJibGFjayIsIHNpemU9My41KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlKCJUb3AgMTAgY291bnRyaWVzIHdpdGggY29uZmlybWVkIGNhc2VzIGFzIG9mICIsbWF4KENPVklEXzIkRGF0ZTIpKSwKICAgICAgIHggPSAiQ29uZmlybWVkIGNhc2VzIiwKICAgICAgIHkgPSAiQ291bnRyeSIpICsKICB0aGVtZV9taW5pbWFsKCkKCmdncGxvdGx5KHBsb3QsdG9vbHRpcCA9IGMoIngiKSx3aWR0aD03NTApCmBgYAoKVGltZSBkaXN0cmlidXRpb246CmBgYHtyfQpDT1ZJRF8yX0RheTwtIENPVklEXzIgJT4lIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXb3JsZF9kZWF0aHM9c3VtKERlYXRocyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV29ybGRfcmVjb3ZlcmVkPXN1bShSZWNvdmVyZWQpKQoKCkNPVklEX0RheV9jb25maXJtZWRfc2VyaWVzPC14dHMoQ09WSURfMl9EYXkkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheSREYXRlMikKQ09WSURfRGF5X2RlYXRoc19zZXJpZXM8LXh0cyhDT1ZJRF8yX0RheSRXb3JsZF9kZWF0aHMsIG9yZGVyLmJ5PUNPVklEXzJfRGF5JERhdGUyKQpDT1ZJRF9EYXlfcmVjb3ZlcmVkX3NlcmllczwteHRzKENPVklEXzJfRGF5JFdvcmxkX3JlY292ZXJlZCwgb3JkZXIuYnk9Q09WSURfMl9EYXkkRGF0ZTIpCgpEYXlfc3VtbWFyeTwtY2JpbmQoQ09WSURfRGF5X2NvbmZpcm1lZF9zZXJpZXMsQ09WSURfRGF5X2RlYXRoc19zZXJpZXMsQ09WSURfRGF5X3JlY292ZXJlZF9zZXJpZXMpCmBgYAoKYGBge3J9CmR5Z3JhcGgoRGF5X3N1bW1hcnksIG1haW4gPSAiU0FSUy1DT1YyLW91dGJyZWFrOiBUb3RhbCB3b3JsZHdpZGUgY2FzZXMiLCAKICAgICAgICB4bGFiPSJEYXRlIiwgeWxhYj0iVG90YWwgY2FzZXMiLHdpZHRoID0gNzUwKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX0RheV9jb25maXJtZWRfc2VyaWVzIiwgIlRvdGFsIGNhc2VzIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMywgY29sb3I9cmdiKDUzLzI1NSwxMTYvMjU1LDE5OS8yNTUpKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX0RheV9kZWF0aHNfc2VyaWVzIiwgIlRvdGFsIGRlYXRocyIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsIGNvbG9yPXJnYigxODkvMjU1LDU1LzI1NSw0OC8yNTUpKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX0RheV9yZWNvdmVyZWRfc2VyaWVzIiwgIlRvdGFsIHJlY292ZXJlZCIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsIGNvbG9yPXJnYig2OS8yNTUsMTM2LzI1NSw1MS8yNTUpKSAlPiUgCiAgZHlSYW5nZVNlbGVjdG9yKCkKYGBgCgoKYGBge3J9Ck5ld19jb3VudDwtZnVuY3Rpb24oeCkKewogIERhaWx5X2Nhc2VzPC1udW1lcmljKGxlbmd0aCh4KSkKICAKICBmb3IoaSBpbiBsZW5ndGgoeCk6MikKICB7CiAgICBEYWlseV9jYXNlc1tpXT14W2ldIC0geFtpLTFdCiAgfQogIHJldHVybihEYWlseV9jYXNlcykKfQoKTmV3X2Nhc2VzPC1OZXdfY291bnQoQ09WSURfMl9EYXkkV29ybGRfY29uZmlybWVkKQpOZXdfZGVhdGhzPC1OZXdfY291bnQoQ09WSURfMl9EYXkkV29ybGRfZGVhdGhzKQpOZXdfcmVjb3ZlcmVkPC1OZXdfY291bnQoQ09WSURfMl9EYXkkV29ybGRfcmVjb3ZlcmVkKQpDT1ZJRF9OZXdfY29uZmlybWVkX3NlcmllczwteHRzKE5ld19jYXNlcywgb3JkZXIuYnk9Q09WSURfMl9EYXkkRGF0ZTIpCkNPVklEX05ld19kZWF0aHNfc2VyaWVzPC14dHMoTmV3X2RlYXRocywgb3JkZXIuYnk9Q09WSURfMl9EYXkkRGF0ZTIpCkNPVklEX05ld19yZWNvdmVyZWRfc2VyaWVzPC14dHMoTmV3X3JlY292ZXJlZCwgb3JkZXIuYnk9Q09WSURfMl9EYXkkRGF0ZTIpCgpOZXdfc3VtbWFyeTwtY2JpbmQoQ09WSURfTmV3X2NvbmZpcm1lZF9zZXJpZXMsQ09WSURfTmV3X2RlYXRoc19zZXJpZXMsQ09WSURfTmV3X3JlY292ZXJlZF9zZXJpZXMpCmBgYAoKYGBge3J9CmR5Z3JhcGgoTmV3X3N1bW1hcnksIG1haW4gPSAiU0FSUy1DT1YyLW91dGJyZWFrOiBUb3RhbCB3b3JsZHdpZGUgY2FzZXMiLCAKICAgICAgICB4bGFiPSJEYXRlIiwgeWxhYj0iTm92ZWwgY29yb25hdmlydXMgY2FzZXMiLHdpZHRoID0gNzUwKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX05ld19jb25maXJtZWRfc2VyaWVzIiwgIk5ldyBjYXNlcyIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsIGNvbG9yPXJnYig1My8yNTUsMTE2LzI1NSwxOTkvMjU1KSkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9OZXdfZGVhdGhzX3NlcmllcyIsICJOZXcgZGVhdGhzIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMywgY29sb3I9cmdiKDE4OS8yNTUsNTUvMjU1LDQ4LzI1NSkpICU+JSAKICBkeVNlcmllcygiQ09WSURfTmV3X3JlY292ZXJlZF9zZXJpZXMiLCAiTmV3IHJlY292ZXJlZCIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsIGNvbG9yPXJnYig2OS8yNTUsMTM2LzI1NSw1MS8yNTUpKSAlPiUgCiAgZHlSYW5nZVNlbGVjdG9yKCkKYGBgCgoKVGVhbSBtZW1iZXJzIGNvdW50cmllcyB0b3RhbCBjYXNlczoKYGBge3J9CkNPVklEXzJfRGF5X0xlYmFub248LSBDT1ZJRF8yICU+JSAKICBmaWx0ZXIoQ291bnRyeS5SZWdpb24gJWluJSBjKCJMZWJhbm9uIikpICU+JSAKICBncm91cF9ieShEYXRlMikgJT4lIHN1bW1hcmlzZShXb3JsZF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCkpCgpDT1ZJRF8yX0RheV9DaGlsZTwtIENPVklEXzIgJT4lIAogIGZpbHRlcihDb3VudHJ5LlJlZ2lvbiAlaW4lIGMoIkNoaWxlIikpICU+JSAKICBncm91cF9ieShEYXRlMikgJT4lIHN1bW1hcmlzZShXb3JsZF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCkpCgpDT1ZJRF8yX0RheV9Db2xvbWJpYTwtIENPVklEXzIgJT4lIAogIGZpbHRlcihDb3VudHJ5LlJlZ2lvbiAlaW4lIGMoIkNvbG9tYmlhIikpICU+JSAKICBncm91cF9ieShEYXRlMikgJT4lIHN1bW1hcmlzZShXb3JsZF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCkpCgpDT1ZJRF8yX0RheV9Db3N0YVJpY2E8LSBDT1ZJRF8yICU+JSAKICBmaWx0ZXIoQ291bnRyeS5SZWdpb24gJWluJSBjKCJDb3N0YSBSaWNhIikpICU+JSAKICBncm91cF9ieShEYXRlMikgJT4lIHN1bW1hcmlzZShXb3JsZF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCkpCgoKQ09WSURfRGF5X3Nlcmllc19MZWJhbm9uPC14dHMoQ09WSURfMl9EYXlfTGViYW5vbiRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0xlYmFub24kRGF0ZTIpCkNPVklEX0RheV9zZXJpZXNfQ2hpbGU8LXh0cyhDT1ZJRF8yX0RheV9DaGlsZSRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0NoaWxlJERhdGUyKQpDT1ZJRF9EYXlfc2VyaWVzX0NvbG9tYmlhPC14dHMoQ09WSURfMl9EYXlfQ29sb21iaWEkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9Db2xvbWJpYSREYXRlMikKQ09WSURfRGF5X3Nlcmllc19Db3N0YVJpY2E8LXh0cyhDT1ZJRF8yX0RheV9Db3N0YVJpY2EkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9Db3N0YVJpY2EkRGF0ZTIpCgpPdXJfQ291bnRyaWVzPC1jYmluZChDT1ZJRF9EYXlfc2VyaWVzX0xlYmFub24sQ09WSURfRGF5X3Nlcmllc19DaGlsZSxDT1ZJRF9EYXlfc2VyaWVzX0NvbG9tYmlhLENPVklEX0RheV9zZXJpZXNfQ29zdGFSaWNhKQoKYGBgCgpgYGB7cn0KZHlncmFwaChPdXJfQ291bnRyaWVzLCBtYWluID0gIlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgY2FzZXMgYnkgY291bnRyeSIsIHhsYWI9IkRhdGUiLCB5bGFiPSJUb3RhbCBjYXNlcyIsd2lkdGggPSA3NTApICU+JSAKICBkeVNlcmllcygiQ09WSURfRGF5X3Nlcmllc19MZWJhbm9uIiwgIkxlYmFub24iLGRyYXdQb2ludHMgPSBUUlVFLCAKICAgICAgICAgICBwb2ludFNpemUgPSAzLCBjb2xvcj1yZ2IoMCwwLDMvMjU1KSkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9EYXlfc2VyaWVzX0NoaWxlIiwgIkNoaWxlIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMyxjb2xvcj1yZ2IoMTIwLzI1NSwyOC8yNTUsMTA5LzI1NSkpICU+JSAKICBkeVNlcmllcygiQ09WSURfRGF5X3Nlcmllc19Db2xvbWJpYSIsICJDb2xvbWJpYSIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsY29sb3I9cmdiKDIzNy8yNTUsMTA1LzI1NSwzNy8yNTUpKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX0RheV9zZXJpZXNfQ29zdGFSaWNhIiwgIkNvc3RhIFJpY2EiLGRyYXdQb2ludHMgPSBUUlVFLAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsY29sb3I9cmdiKDIwNC8yNTUsMTk3LzI1NSwxMjYvMjU1KSkgJT4lIAogIGR5UmFuZ2VTZWxlY3RvcigpCmBgYAoKYGBge3J9Ck5ld19MZWJhbm9uPC1OZXdfY291bnQoQ09WSURfMl9EYXlfTGViYW5vbiRXb3JsZF9jb25maXJtZWQpCk5ld19DaGlsZTwtTmV3X2NvdW50KENPVklEXzJfRGF5X0NoaWxlJFdvcmxkX2NvbmZpcm1lZCkKTmV3X0NvbG9tYmlhPC1OZXdfY291bnQoQ09WSURfMl9EYXlfQ29sb21iaWEkV29ybGRfY29uZmlybWVkKQpOZXdfQ29zdGFSaWNhPC1OZXdfY291bnQoQ09WSURfMl9EYXlfQ29zdGFSaWNhJFdvcmxkX2NvbmZpcm1lZCkKCkNPVklEX05ld19zZXJpZXNfTGViYW5vbjwteHRzKE5ld19MZWJhbm9uLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9MZWJhbm9uJERhdGUyKQpDT1ZJRF9OZXdfc2VyaWVzX0NoaWxlPC14dHMoTmV3X0NoaWxlLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9DaGlsZSREYXRlMikKQ09WSURfTmV3X3Nlcmllc19Db2xvbWJpYTwteHRzKE5ld19Db2xvbWJpYSwgb3JkZXIuYnk9Q09WSURfMl9EYXlfQ29sb21iaWEkRGF0ZTIpCkNPVklEX05ld19zZXJpZXNfQ29zdGFSaWNhPC14dHMoTmV3X0Nvc3RhUmljYSwgb3JkZXIuYnk9Q09WSURfMl9EYXlfQ29zdGFSaWNhJERhdGUyKQoKT3VyX05ld19Db3VudHJpZXM8LWNiaW5kKENPVklEX05ld19zZXJpZXNfTGViYW5vbixDT1ZJRF9OZXdfc2VyaWVzX0NoaWxlLENPVklEX05ld19zZXJpZXNfQ29sb21iaWEsQ09WSURfTmV3X3Nlcmllc19Db3N0YVJpY2EpCmBgYAoKYGBge3J9CmR5Z3JhcGgoT3VyX05ld19Db3VudHJpZXMsIG1haW4gPSAiU0FSUy1DT1YyLW91dGJyZWFrOiBOZXcgY2FzZXMgYnkgY291bnRyeSIsIHhsYWI9IkRhdGUiLCB5bGFiPSJUb3RhbCBjYXNlcyIsd2lkdGggPSA3NTApICU+JSAKICBkeVNlcmllcygiQ09WSURfTmV3X3Nlcmllc19MZWJhbm9uIiwgIkxlYmFub24iLGRyYXdQb2ludHMgPSBUUlVFLCAKICAgICAgICAgICBwb2ludFNpemUgPSAzLCBjb2xvcj1yZ2IoMCwwLDMvMjU1KSkgJT4lIAogIGR5U2VyaWVzKCJDT1ZJRF9OZXdfc2VyaWVzX0NoaWxlIiwgIkNoaWxlIixkcmF3UG9pbnRzID0gVFJVRSwgCiAgICAgICAgICAgcG9pbnRTaXplID0gMyxjb2xvcj1yZ2IoMTIwLzI1NSwyOC8yNTUsMTA5LzI1NSkpICU+JSAKICBkeVNlcmllcygiQ09WSURfTmV3X3Nlcmllc19Db2xvbWJpYSIsICJDb2xvbWJpYSIsZHJhd1BvaW50cyA9IFRSVUUsIAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsY29sb3I9cmdiKDIzNy8yNTUsMTA1LzI1NSwzNy8yNTUpKSAlPiUgCiAgZHlTZXJpZXMoIkNPVklEX05ld19zZXJpZXNfQ29zdGFSaWNhIiwgIkNvc3RhIFJpY2EiLGRyYXdQb2ludHMgPSBUUlVFLAogICAgICAgICAgIHBvaW50U2l6ZSA9IDMsY29sb3I9cmdiKDIwNC8yNTUsMTk3LzI1NSwxMjYvMjU1KSkgJT4lIAogIGR5UmFuZ2VTZWxlY3RvcigpCmBgYAoKIyBMb29raW5nIGZvciBjb3JyZWxhdGlvbnMKCmBgYHtyfQpmaWcgPC0gcGxvdF9seShDT1ZJRF91cGRhdGVkLCB4ID0gfkNvbmZpcm1lZCwgeSA9IH5EZWF0aHMsIHogPSB+UmVjb3ZlcmVkLCB3aWR0aD03NTApICU+JSAKICBhZGRfbWFya2Vycyh0ZXh0PSB+Q291bnRyeS5SZWdpb24gLGhvdmVyaW5mbz0gInRleHQiLAogICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3I9cmdiKDE4OS8yNTUsNTUvMjU1LDQ4LzI1NSkpKSAlPiUgCiAgbGF5b3V0KHRpdGxlPSJDb25maXJtZWQgY2FzZXMgVnMuIERlYXRocyBWcy4gUmVjb3ZlcmVkIiwgc2NlbmUgPSBsaXN0KAogICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICdDb25maXJtZWQnKSwKICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gJ0RlYXRocycpLAogICAgICAgICAgICAgICAgICAgICB6YXhpcyA9IGxpc3QodGl0bGUgPSAnUmVjb3ZlcmVkJykpKSAKZmlnCmBgYAojIyBGb3IgdGhlIG51bWJlciBvZiBjYXNlcwoKIyMjIEh1bWFuIERldmVsb3BtZW50IEluZGV4CgpgYGB7cn0KSERJPC1yZWFkLmNzdigiSHVtYW4gRGV2ZWxvcG1lbnQgSW5kZXggKEhESSlfMi5jc3YiLHNlcD0iOyIsZGVjPSIsIikKYGBgCgpgYGB7cn0KQ09WSURfQ291bnRyeTwtQ09WSURfMiAlPiUgZmlsdGVyKERhdGUyPT1tYXgoRGF0ZTIpKSAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeS5SZWdpb24pICU+JSBzdW1tYXJpc2UoVG90YWxfY29uZmlybWVkPXN1bShDb25maXJtZWQpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRvdGFsX2RlYXRocz1zdW0oRGVhdGhzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUb3RhbF9SZWNvdmVyZWQ9c3VtKFJlY292ZXJlZCkpCmBgYAoKUmVtb3ZlIGFmdGVyIHBhcmVudGhlc2VzOgpgYGB7cn0KSERJJENvdW50cnlfMjwtZ3N1YigiXFxzKlxcKFteXFwpXStcXCkiLCIiLGFzLmNoYXJhY3RlcihIREkkQ291bnRyeSkpCmBgYAoKYGBge3J9CkhESSRDb3VudHJ5XzJbSERJJENvdW50cnlfMj09IlVuaXRlZCBTdGF0ZXMiXTwtIlVTIgpIREkkQ291bnRyeV8yW0hESSRDb3VudHJ5XzI9PSJLb3JlYSJdPC0iU291dGggS29yZWEiCmBgYAoKUG9wdWxhdGlvbjoKYGBge3J9ClBvcHVsYXRpb248LXJlYWQuY3N2KCJXb3JsZF9wb3B1bGF0aW9uLmNzdiIsc2VwPSI7IixkZWM9IiwiKQpgYGAKClJlbW92ZSBhZnRlciBjb21tbWE6CmBgYHtyfQpQb3B1bGF0aW9uJENvdW50cnlfTmFtZV8yPC1nc3ViKCIsLioiLCAiIiwgYXMuY2hhcmFjdGVyKFBvcHVsYXRpb24kQ291bnRyeV9OYW1lKSkKYGBgCgpgYGB7cn0KUG9wdWxhdGlvbiRDb3VudHJ5X05hbWVfMltQb3B1bGF0aW9uJENvdW50cnlfTmFtZV8yPT0iVW5pdGVkIFN0YXRlcyJdPC0iVVMiClBvcHVsYXRpb24kQ291bnRyeV9OYW1lXzJbUG9wdWxhdGlvbiRDb3VudHJ5X0NvZGU9PSJLT1IiXTwtIlNvdXRoIEtvcmVhIgpQb3B1bGF0aW9uJENvdW50cnlfTmFtZV8yW1BvcHVsYXRpb24kQ291bnRyeV9Db2RlPT0iQ1pFIl08LSJDemVjaGlhIgpgYGAKCk5hdHVyYWwgSm9pbjoKYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQpDT1ZJRF8zPC0gQ09WSURfQ291bnRyeSAlPiUgaW5uZXJfam9pbihIREksYnk9YygiQ291bnRyeS5SZWdpb24iPSJDb3VudHJ5XzIiKSkgJT4lIAogIGlubmVyX2pvaW4oUG9wdWxhdGlvbixieT1jKCJDb3VudHJ5LlJlZ2lvbiI9IkNvdW50cnlfTmFtZV8yIikpICU+JSAKICBzZWxlY3QoQ291bnRyeS5SZWdpb24sVG90YWxfY29uZmlybWVkLFRvdGFsX2RlYXRocyxUb3RhbF9SZWNvdmVyZWQsSERJX1JhbmtfMjAxOCxZZWFyXzIwMTgsCiAgICAgICAgIENvdW50cnlfQ29kZSxQb3B1bGF0aW9uXzIwMTgpICU+JSAKICBtdXRhdGUoQ2FzZXNfbWlsbGlvbj0oVG90YWxfY29uZmlybWVkL1BvcHVsYXRpb25fMjAxOCkqMTAwMDAwMCwKICAgICAgICAgUmVjb3ZlcmVkX3BlcmNlbnRhZ2U9KFRvdGFsX1JlY292ZXJlZC9Ub3RhbF9jb25maXJtZWQpKjEwMCkgIAoKQ09WSURfMzwtQ09WSURfM1shaXMubmEoQ09WSURfMyRQb3B1bGF0aW9uXzIwMTgpLF0KCmBgYAoKQ291bnRyaWVzIHdpdGggdG9wIDEwIGNhc2VzIHBlciBtaWxsaW9uIGluaGFiaXRhbnRzCmBgYHtyfQpDT1ZJRDNfdG9wPC1DT1ZJRF8zICU+JSB0b3BfbigxMCxDYXNlc19taWxsaW9uKSAlPiUgYXJyYW5nZShkZXNjKENhc2VzX21pbGxpb24pKSAlPiUgCiAgbXV0YXRlKENhc2VzX21pbGxpb249cm91bmQoQ2FzZXNfbWlsbGlvbiwwKSkKYGBgCgpgYGB7cn0KcGxvdDwtZ2dwbG90KGRhdGE9Q09WSUQzX3RvcAogICAgICAgLCBhZXMoeD1DYXNlc19taWxsaW9uLHk9cmVvcmRlcihDb3VudHJ5LlJlZ2lvbixDYXNlc19taWxsaW9uKSkpICsKICBnZW9tX2JhcihzdGF0ID0iaWRlbnRpdHkiLGFscGhhPTAuOCxmaWxsPSJza3kgYmx1ZSIsICkgKwogIGdlb21fdGV4dChhZXMobGFiZWw9Q2FzZXNfbWlsbGlvbiksIHZqdXN0PTAuNSwgaGp1c3Q9MC45LGNvbG9yPSJibGFjayIsIHNpemU9My41KSArCiAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscyA9IGNvbW1hKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlKCJUb3AgMTAgY291bnRyaWVzIHdpdGggY29uZmlybWVkIGNhc2VzIHBlciBtaWxsaW9uIGhhYml0YW50IFxuIGFzIG9mICIsbWF4KENPVklEXzIkRGF0ZTIpKSwKICAgICAgIHggPSAiQ29uZmlybWVkIGNhc2VzIHBlciBtaWxsaW9uIGhhYml0YW50cyIsCiAgICAgICB5ID0gIkNvdW50cnkiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpnZ3Bsb3RseShwbG90LHRvb2x0aXAgPSBjKCJ4Iiksd2lkdGg9NzUwKQpgYGAKCgpQbG90IHRoZSBIdW1hbiBEZXZlbG9wbWVudCBJbmRleChIREkpIFZzLiB0aGUgbnVtYmVyIG9mIGNhc2VzIChhcHBseWluZyBhIGxvZyB0cmFuc2Zvcm1hdGlvbiksIGFuZCB0aGUgcHJvcG9ydGlvbiBvZiByZWNvdmVyZWQgY2FzZXM6CmBgYHtyfQpwbG90PC1nZ3Bsb3QoZGF0YT1DT1ZJRF8zLGFlcyh4PWxvZyhDYXNlc19taWxsaW9uKSx5PVllYXJfMjAxOCwKICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT1SZWNvdmVyZWRfcGVyY2VudGFnZSx0ZXh0PUNvdW50cnkuUmVnaW9uKSkgKwogIGdlb21fcG9pbnQoY29sb3I9ImJsYWNrIixmaWxsPXJnYigyMzcvMjU1LDEwNS8yNTUsMzcvMjU1KSxzaGFwZT0yMSxhbHBoYT0wLjYpICsKICBzY2FsZV9zaXplKHJhbmdlID0gYygzLDE1KSwgbmFtZT0iUmVjb3ZlcmVkIFxuIHBlcmNlbnRhZ2UiKSArCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArCiAgbGFicyh0aXRsZT0iSERJIFZzLiBsb2dhcml0aG11cyBvZiBDT1ZJRC0xOSBjYXNlcyBieSBtaWxsaW9uIGluaGFiaXRhbnRzIFxuIGFuZCBwcm9wb3J0aW9uIG9mIHJlY292ZXJlZCIsCiAgICAgICB4PSJsbihDYXNlcy8xTSBwb3B1bGF0aW9uKSIsCiAgICAgICB5PSJIREkiKQoKZ2dwbG90bHkocGxvdCx0b29sdGlwID0gYygidGV4dCIpLHdpZHRoPTc1MCkKYGBgCgoKYGBge3J9CkNPVklEX251bWVyaWNfMTwtQ09WSURfMyAlPiUgbXV0YXRlKExvZ19jYXNlcz1sb2coQ2FzZXNfbWlsbGlvbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlYXRoX3BlcmNlbnRhZ2U9KFRvdGFsX2RlYXRocy9Ub3RhbF9jb25maXJtZWQpKjEwMCkgJT4lIAogIHNlbGVjdChMb2dfY2FzZXMsUmVjb3ZlcmVkX3BlcmNlbnRhZ2UsRGVhdGhfcGVyY2VudGFnZSxZZWFyXzIwMTgpCgpjb3JycGxvdChjb3IoQ09WSURfbnVtZXJpY18xKSxtZXRob2QgPSAibnVtYmVyIix0bC5jb2w9ImJsYWNrIix0bC5zcnQ9MTUsCiAgICAgICAgIGNvbD1jb2xvclJhbXBQYWxldHRlKGMocmdiKDIwNC8yNTUsMTk3LzI1NSwxMjYvMjU1KSxyZ2IoMjM3LzI1NSwxMDUvMjU1LDM3LzI1NSkpKSgyMDApKQpgYGAKCmBgYHtyfQpNZWFzbGVzPC1yZWFkLmNzdigiTWVhc2xlc19pbW11bml6YXRpb24uY3N2IixzZXA9IjsiLGRlYz0iLiIpCmBgYAoKYGBge3J9Ck1lYXNsZXMkQ291bnRyeV8yPC1nc3ViKCJcXHMqXFwoW15cXCldK1xcKSIsIiIsYXMuY2hhcmFjdGVyKE1lYXNsZXMkQ291bnRyeSkpCmBgYAoKYGBge3J9Ck1lYXNsZXMkQ291bnRyeV8yW01lYXNsZXMkQ291bnRyeV8yPT0iVW5pdGVkIFN0YXRlcyJdPC0iVVMiCk1lYXNsZXMkQ291bnRyeV8yW01lYXNsZXMkQ291bnRyeV8yPT0iS29yZWEiXTwtIlNvdXRoIEtvcmVhIgpgYGAKCmBgYHtyfQpDT1ZJRF8zPC0gQ09WSURfMyAlPiUgaW5uZXJfam9pbihNZWFzbGVzLGJ5PWMoIkNvdW50cnkuUmVnaW9uIj0iQ291bnRyeV8yIikpICU+JSBzZWxlY3QoLWMoIkNvdW50cnkiKSkKYGBgCgoKIyMjIEhlYWx0aCBleHBlbmRpdHVyZSAoJSBvZiBHRFApCgpgYGB7cn0KSGVhbHRoX2V4cGVuZGl0dXJlPC1yZWFkLmNzdigiSGVhbHRoX2V4cGVuZGl0dXJlX0dEUC5jc3YiLHNlcD0iOyIsZGVjPSIuIikKYGBgCgpgYGB7cn0KSGVhbHRoX2V4cGVuZGl0dXJlJENvdW50cnlfMjwtZ3N1YigiXFxzKlxcKFteXFwpXStcXCkiLCIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihIZWFsdGhfZXhwZW5kaXR1cmUkQ291bnRyeSkpCmBgYAoKYGBge3J9CkhlYWx0aF9leHBlbmRpdHVyZSRDb3VudHJ5XzJbSGVhbHRoX2V4cGVuZGl0dXJlJENvdW50cnlfMj09IlVuaXRlZCBTdGF0ZXMiXTwtIlVTIgpIZWFsdGhfZXhwZW5kaXR1cmUkQ291bnRyeV8yW0hlYWx0aF9leHBlbmRpdHVyZSRDb3VudHJ5XzI9PSJLb3JlYSJdPC0iU291dGggS29yZWEiCmBgYAoKCmBgYHtyfQpDT1ZJRF8zPC0gQ09WSURfMyAlPiUgaW5uZXJfam9pbihIZWFsdGhfZXhwZW5kaXR1cmUsYnk9YygiQ291bnRyeS5SZWdpb24iPSJDb3VudHJ5XzIiKSkgJT4lIHNlbGVjdCgtYygiQ291bnRyeSIpKQpgYGAKCmBgYHtyfQpwbG90PC1nZ3Bsb3QoZGF0YT1DT1ZJRF8zLGFlcyh4PWxvZyhDYXNlc19taWxsaW9uKSx5PUV4cGVuZGl0dXJlXzIwMTYsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9UmVjb3ZlcmVkX3BlcmNlbnRhZ2UsdGV4dD1Db3VudHJ5LlJlZ2lvbikpICsKICBnZW9tX3BvaW50KGNvbG9yPSJibGFjayIsZmlsbD1yZ2IoMjA0LzI1NSwxOTcvMjU1LDEyNi8yNTUpLHNoYXBlPTIxLGFscGhhPTAuNikgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDMsMTUpLCBuYW1lPSJSZWNvdmVyZWQgXG4gcGVyY2VudGFnZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICBsYWJzKHRpdGxlPSJIZWFsdGggZXhwZW5kaXR1cmUgKCUgb2YgR0RQKSBWcy4gbG9nYXJpdGhtdXMgb2YgQ09WSUQtMTkgY2FzZXMgYnkgbWlsbGlvbiBpbmhhYml0YW50cyBcbiBhbmQgcHJvcG9ydGlvbiBvZiByZWNvdmVyZWQiLAogICAgICAgeD0ibG4oQ2FzZXMvMU0gcG9wdWxhdGlvbikiLAogICAgICAgeT0iJSBvZiBHRFAgaW4gaGVhbHRoIikKCmdncGxvdGx5KHBsb3QsdG9vbHRpcCA9IGMoInRleHQiKSx3aWR0aD03NTApCmBgYAoKIyMjIFRlbXBlcmF0dXJlCgpgYGB7cn0KVGVtcGVyYXR1cmU8LXJlYWQuY3N2KCJHbG9iYWxMYW5kVGVtcGVyYXR1cmVzQnlDb3VudHJ5LmNzdiIsc2VwPSI7IikKYGBgCgpgYGB7cn0KRGF0ZV9UZW1wPC1hcy5EYXRlKFRlbXBlcmF0dXJlJGR0LCBmb3JtYXQ9IiVkLyVtLyVZIikgCgpUZW1wZXJhdHVyZSREYXRlX1RlbXA8LURhdGVfVGVtcApgYGAKCkV4dHJhY3QgbW9udGgsIGZpbHRlciBJUSAoSmFuLCBGZWIgYW5kIE1hcikgYW5kIG9idGFpbiB0aGUgYXZlcmFnZSBJUSB0ZW1wZXJhdHVyZToKYGBge3J9ClRlbXBlcmF0dXJlPC0gVGVtcGVyYXR1cmUgJT4lIG11dGF0ZShNb250aD1tb250aChUZW1wZXJhdHVyZSREYXRlX1RlbXAsbGFiZWwgPVRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWWVhcj15ZWFyKFRlbXBlcmF0dXJlJERhdGVfVGVtcCkpICU+JSAKICBmaWx0ZXIoTW9udGggJWluJSBjKCJKYW4iLCJGZWIiLCJNYXIiKSAmIFllYXI+PTIwMDApICAlPiUgCiAgZ3JvdXBfYnkoQ291bnRyeSkgJT4lIHN1bW1hcmlzZShBdmdfSVFfdGVtcGVyYXR1cmU9bWVhbihBdmVyYWdlVGVtcGVyYXR1cmUpKQoKVGVtcGVyYXR1cmU8LVRlbXBlcmF0dXJlWyFpcy5uYShUZW1wZXJhdHVyZSRBdmdfSVFfdGVtcGVyYXR1cmUpLF0KYGBgCgpgYGB7cn0KVGVtcGVyYXR1cmUkQ291bnRyeV8yPC1nc3ViKCJcXHMqXFwoW15cXCldK1xcKSIsIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKFRlbXBlcmF0dXJlJENvdW50cnkpKQpgYGAKCmBgYHtyfQpUZW1wZXJhdHVyZSRDb3VudHJ5XzJbVGVtcGVyYXR1cmUkQ291bnRyeV8yPT0iVW5pdGVkIFN0YXRlcyJdPC0iVVMiClRlbXBlcmF0dXJlJENvdW50cnlfMltUZW1wZXJhdHVyZSRDb3VudHJ5XzI9PSJLb3JlYSJdPC0iU291dGggS29yZWEiCmBgYAoKYGBge3J9CkNPVklEXzM8LSBDT1ZJRF8zICU+JSBpbm5lcl9qb2luKFRlbXBlcmF0dXJlLGJ5PWMoIkNvdW50cnkuUmVnaW9uIj0iQ291bnRyeV8yIikpICU+JSBzZWxlY3QoLWMoIkNvdW50cnkiKSkKCiN3cml0ZS5jc3YoQ09WSURfMywiQ09WSURfQ292YXJpYWJsZXMuY3N2IikKYGBgCgpgYGB7cn0KcGxvdDwtZ2dwbG90KGRhdGE9Q09WSURfMyxhZXMoeD1sb2coQ2FzZXNfbWlsbGlvbikseT1BdmdfSVFfdGVtcGVyYXR1cmUsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9UmVjb3ZlcmVkX3BlcmNlbnRhZ2UsdGV4dD1Db3VudHJ5LlJlZ2lvbikpICsKICBnZW9tX3BvaW50KGNvbG9yPSJibGFjayIsZmlsbD1yZ2IoMTIwLzI1NSwyOC8yNTUsMTA5LzI1NSksc2hhcGU9MjEsYWxwaGE9MC42KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMywxNSksIG5hbWU9IlJlY292ZXJlZCBcbiBwZXJjZW50YWdlIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogIGxhYnModGl0bGU9IkF2ZXJhZ2UgSVEgdGVtcGVyYXR1cmUgVnMuIGxvZ2FyaXRobXVzIG9mIENPVklELTE5IGNhc2VzIGJ5IG1pbGxpb24gaW5oYWJpdGFudHMgXG4gYW5kIHByb3BvcnRpb24gb2YgcmVjb3ZlcmVkIiwKICAgICAgIHg9ImxuKENhc2VzLzFNIHBvcHVsYXRpb24pIiwKICAgICAgIHk9IlRlbXBlcmF0dXJlIChDZWxjaXVzKSIpCgpnZ3Bsb3RseShwbG90LHRvb2x0aXAgPSBjKCJ0ZXh0Iiksd2lkdGg9NzUwKQpgYGAKCiMjIEZvciB0aGUgbnVtYmVyIG9mIGRlYXRocwoKIyMjRFRQIGltbXVuaXphdGlvbgoKYGBge3J9CkRUUF9pbW11bml6YXRpb248LXJlYWQuY3N2KCJJbmZhbnRzX2xhY2tpbmdfaW1tdW5pemF0aW9uX0RUUC5jc3YiLHNlcD0iOyIpCmBgYAoKUmVtb3ZlIGFmdGVyIHBhcmVudGhlc2VzOgpgYGB7cn0KRFRQX2ltbXVuaXphdGlvbiRDb3VudHJ5XzI8LWdzdWIoIlxccypcXChbXlxcKV0rXFwpIiwiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKERUUF9pbW11bml6YXRpb24kQ291bnRyeSkpCmBgYAoKCmBgYHtyfQpEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMltEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMj09IlVuaXRlZCBTdGF0ZXMiXTwtIlVTIgpEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMltEVFBfaW1tdW5pemF0aW9uJENvdW50cnlfMj09IktvcmVhIl08LSJTb3V0aCBLb3JlYSIKYGBgCgoKYGBge3Isd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQpDT1ZJRF80PC0gQ09WSURfQ291bnRyeSAlPiUgaW5uZXJfam9pbihEVFBfaW1tdW5pemF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieT1jKCJDb3VudHJ5LlJlZ2lvbiI9IkNvdW50cnlfMiIpKSAlPiUgCiAgc2VsZWN0KENvdW50cnkuUmVnaW9uLFRvdGFsX2NvbmZpcm1lZCxUb3RhbF9kZWF0aHMsVG90YWxfUmVjb3ZlcmVkLAogICAgICAgICBMYWNrX0RUUF9pbm1tdW5pemF0aW9uXzIwMTgpICU+JSAKICBtdXRhdGUoUmVjb3ZlcmVkX3BlcmNlbnRhZ2U9KFRvdGFsX1JlY292ZXJlZC9Ub3RhbF9jb25maXJtZWQpKjEwMCwKICAgICAgICAgRGVhdGhfcmF0ZT0oVG90YWxfZGVhdGhzL1RvdGFsX2NvbmZpcm1lZCkqMTAwKQpgYGAKCmBgYHtyfQpwbG90PC1nZ3Bsb3QoZGF0YT1DT1ZJRF80LGFlcyh4PURlYXRoX3JhdGUseT1MYWNrX0RUUF9pbm1tdW5pemF0aW9uXzIwMTgsCiAgICAgICAgICAgICAgICAgICAgICAgIHNpemU9UmVjb3ZlcmVkX3BlcmNlbnRhZ2UsdGV4dD1Db3VudHJ5LlJlZ2lvbikpICsKICBnZW9tX3BvaW50KGNvbG9yPSJibGFjayIsZmlsbD1yZ2IoMTIwLzI1NSwyOC8yNTUsMTA5LzI1NSksc2hhcGU9MjEsYWxwaGE9MC42KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMywxNSksIG5hbWU9IlJlY292ZXJlZCBcbiBwZXJjZW50YWdlIikgKwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKwogIGxhYnModGl0bGU9IiUgaW5mYW50cyBsYWNraW5nIERUUCBpbW11bml6YXRpb24gVnMuIGRlYXRoIHJhdGUgYW5kIFxuIHByb3BvcnRpb24gb2YgcmVjb3ZlcmVkIiwKICAgICAgIHg9Ik5vdmVsIGNvcm9uYXZpcnVzIGRlYXRoIHJhdGUiLAogICAgICAgeT0iJSBvZiBpbmZhbnRzIikKCmdncGxvdGx5KHBsb3QsdG9vbHRpcCA9IGMoInRleHQiKSx3aWR0aD03NTApCmBgYAoKIyMjIEluZmFudHMgbGFja2luZyBpbW11bml6YXRpb24sIG1lYXNsZXMgKCUgb2Ygb25lLXllYXItb2xkcykKCmBgYHtyfQpDT1ZJRF80PC0gQ09WSURfNCAlPiUgaW5uZXJfam9pbihNZWFzbGVzLGJ5PWMoIkNvdW50cnkuUmVnaW9uIj0iQ291bnRyeV8yIikpICU+JSBzZWxlY3QoLWMoIkNvdW50cnkiKSkKYGBgCgoKYGBge3J9CmdncGxvdChDT1ZJRF80LCBhZXMoeT1NZWFzbGVzXzIwMTgpKSArIAogIGdlb21fYm94cGxvdChmaWxsPSJkb2RnZXJibHVlNCIsb3V0bGllci5zaGFwZSA9IDIxLCAKICAgICAgICAgICAgICAgb3V0bGllci5maWxsID0gImZpcmVicmljayIsYWxwaGE9MC43NSkgKwogIGdndGl0bGUoIkJveHBsb3Qgb2YgJSBpbmZhbnRzIGxhY2tpbmcgbWVhc2xlcyBpbW11bml6YXRpb24iKSArIHlsYWIoIiUgb2YgaW5mYW50cyIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KZ2dwbG90KENPVklEXzQsIGFlcyhNZWFzbGVzXzIwMTgpKSArIAogIGdlb21faGlzdG9ncmFtKGZpbGw9ImRvZGdlcmJsdWU0IixiaW5zPTIwLGFscGhhPTAuOCkgKwogIGdndGl0bGUoIkhpc3RvZ3JhbSBvZiAlIGluZmFudHMgbGFja2luZyBtZWFzbGVzIGltbXVuaXphdGlvbiIpICsgCiAgeGxhYigiJSBvZiBpbmZhbnRzIikgKyAKICB5bGFiKCJDb3VudCIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpgYGB7cn0KcGxvdDwtZ2dwbG90KGRhdGE9Q09WSURfNCxhZXMoeD1EZWF0aF9yYXRlLHk9TWVhc2xlc18yMDE4LAogICAgICAgICAgICAgICAgICAgICAgICBzaXplPVJlY292ZXJlZF9wZXJjZW50YWdlLHRleHQ9Q291bnRyeS5SZWdpb24pKSArCiAgZ2VvbV9wb2ludChjb2xvcj0iYmxhY2siLGZpbGw9cmdiKDIzNy8yNTUsMTA1LzI1NSwzNy8yNTUpLHNoYXBlPTIxLGFscGhhPTAuNikgKwogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDMsMTUpLCBuYW1lPSJSZWNvdmVyZWQgXG4gcGVyY2VudGFnZSIpICsKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpICsKICBsYWJzKHRpdGxlPSIlIGluZmFudHMgbGFja2luZyBtZWFzbGVzIGltbXVuaXphdGlvbiBWcy4gZmF0YWxpdHkgcmF0ZSBcbiBhbmQgcHJvcG9ydGlvbiBvZiByZWNvdmVyZWQiLAogICAgICAgeD0iRmF0YWxpdHkgcmF0ZSAoJSkiLAogICAgICAgeT0iJSBvZiBpbmZhbnRzIikKCmdncGxvdGx5KHBsb3QsdG9vbHRpcCA9IGMoInRleHQiKSx3aWR0aD03NTApCmBgYAoKCiMgRml0dGluZyBhIHJlZ3Jlc3Npb24gbW9kZWwKClRyYW5zZm9ybWluZyB3aXRoIGxuIGFuZCBjb252ZXJ0aW5nIHRlbXBlcmF0dXJlIGFzIGtlbHZpbjoKYGBge3J9Ck1vZDE8LWxtKGxvZyhDT1ZJRF8zJENhc2VzX21pbGxpb24pfmxvZyhDT1ZJRF8zJFllYXJfMjAxOCkrbG9nKENPVklEXzMkTWVhc2xlc18yMDE4KSsKICAgICAgICAgICBsb2coQ09WSURfMyRFeHBlbmRpdHVyZV8yMDE2KStsb2coQ09WSURfMyRBdmdfSVFfdGVtcGVyYXR1cmUrMjczLjE1KSkKc3VtbWFyeShNb2QxKQpgYGAKClN0ZXB3aXNlIHdpdGggQUlDIGNyaXRlcnRpb246CmBgYHtyfQpNb2QyPC1zdGVwKE1vZDEsZGlyZWN0aW9uID0gImJvdGgiKQpgYGAKCmBgYHtyfQpzdW1tYXJ5KE1vZDIpCmBgYAoKTm9ybWFsaXR5IG9mIHJlc2lkdWFsczoKYGBge3J9Cmhpc3QoTW9kMiRyZXNpZHVhbHMpCnNoYXBpcm8udGVzdChNb2QyJHJlc2lkdWFscykKYGBgCgpQcmVkaWN0aW9uIHBvd2VyOiBzZXBhcmF0ZSBiZXR3ZWVuIHRyYWluaW5nIGFuZCB0ZXN0aW5nOgpgYGB7cn0Kc2V0LnNlZWQoMTc5ODE5KQpTYW1wbGUgPC0gc2FtcGxlKDE6bGVuZ3RoKENPVklEXzMkQ2FzZXNfbWlsbGlvbiksbGVuZ3RoKENPVklEXzMkQ2FzZXNfbWlsbGlvbikqMC4yMCkKdC50ZXN0aW5nPC0gQ09WSURfM1tTYW1wbGUsXQp0LnRyYWluaW5nPC0gQ09WSURfM1stU2FtcGxlLF0KYGBgCgpUcmFuc2Zvcm0gdGhlIHRyYWluaW5nIGFuZCB0ZXN0aW5nIHZhcmlhYmxlcyBhcyBiZWZvcmU6CmBgYHtyfQp0LnRyYWluaW5nPC10LnRyYWluaW5nICU+JSBtdXRhdGUoQ2FzZXNfbWlsbGlvbl9sb2c9bG9nKENhc2VzX21pbGxpb24pLEhESV9sb2c9bG9nKFllYXJfMjAxOCksCiAgICAgICAgICAgICAgICAgICAgICBHRFBfbG9nPWxvZyhFeHBlbmRpdHVyZV8yMDE2KSwKICAgICAgICAgICAgICAgICAgICAgIFRlbXBlcmF0dXJlX2xvZ19rZWx2aW49bG9nKEF2Z19JUV90ZW1wZXJhdHVyZSsyNzMuMTUpKSAKCnQudHJhaW5pbmc8LXQudHJhaW5pbmdbLDE0OjE3XQoKdC50ZXN0aW5nPC10LnRlc3RpbmcgJT4lIG11dGF0ZShDYXNlc19taWxsaW9uX2xvZz1sb2coQ2FzZXNfbWlsbGlvbiksSERJX2xvZz1sb2coWWVhcl8yMDE4KSwKICAgICAgICAgICAgICAgICAgICAgIEdEUF9sb2c9bG9nKEV4cGVuZGl0dXJlXzIwMTYpLAogICAgICAgICAgICAgICAgICAgICAgVGVtcGVyYXR1cmVfbG9nX2tlbHZpbj1sb2coQXZnX0lRX3RlbXBlcmF0dXJlKzI3My4xNSkpIAoKdC50ZXN0aW5nPC10LnRlc3RpbmdbLDE0OjE3XQpgYGAKCkZpdCB0aGUgc2FtZSBtb2RlbCB3aXRoIHRyYWluaW5nCmBgYHtyfQpNb2QzPC1sbShDYXNlc19taWxsaW9uX2xvZ34uLCBkYXRhPXQudHJhaW5pbmcpCnN1bW1hcnkoTW9kMykKYGBgCgpFcnJvciBmdW5jdGlvbnM6CmBgYHtyfQojIFJlc2lkdWFsIFN1bSBvZiBTcXVhcmUgKFJTUykKUlNTPC1mdW5jdGlvbihQcmVkLEFjdHVhbCkgewogIHNzPC1zdW0oKEFjdHVhbC1QcmVkKV4yKQogIHJldHVybihzcykKfQoKIyBSZXNpZHVhbCBTdGFuZGFyZCBFcnJvciAoUlNFKQpSU0U8LWZ1bmN0aW9uKFByZWQsUmVhbCxOdW1QcmVkKSB7CiAgTjwtbGVuZ3RoKFJlYWwpLU51bVByZWQtMSAgCiAgc3M8LXNxcnQoKDEvTikqUlNTKFByZWQsUmVhbCkpCiAgcmV0dXJuKHNzKQp9CiMgTWVhbiBTcXVhcmVkIEVycm9yIApNU0UgPC0gZnVuY3Rpb24oUHJlZCxBY3R1YWwpIHsKICBOPC1sZW5ndGgoQWN0dWFsKQogIHNzPC0oMS9OKSpSU1MoUHJlZCxBY3R1YWwpCiAgcmV0dXJuKHNzKQp9CgojIFJlbGF0aXZlIGVycm9yClJlbGF0aXZlRXJyb3I8LWZ1bmN0aW9uKFByZWQsQWN0dWFsKSB7CiAgc3M8LXN1bShhYnMoQWN0dWFsLVByZWQpKS9zdW0oYWJzKEFjdHVhbCkpCiAgcmV0dXJuKHNzKQp9CmBgYAoKUHJlZGljdGlvbjoKYGBge3J9ClByZWQ8LXByZWRpY3QoTW9kMyx0LnRlc3RpbmcpCmBgYAoKRXJyb3JzOgpgYGB7cn0KUlNTX01vZDM8LVJTUyhQcmVkLHQudGVzdGluZyRDYXNlc19taWxsaW9uX2xvZykKUlNFX01vZDM8LVJTRShQcmVkLHQudGVzdGluZyRDYXNlc19taWxsaW9uX2xvZyxkaW0odC50ZXN0aW5nKVsyXS0xKQpNU0VfTW9kMzwtTVNFKFByZWQsdC50ZXN0aW5nJENhc2VzX21pbGxpb25fbG9nKQpSZWxhdGl2ZUVycm9yX01vZDM8LVJlbGF0aXZlRXJyb3IoUHJlZCx0LnRlc3RpbmckQ2FzZXNfbWlsbGlvbl9sb2cpCgpNb2QzX0Vycm9yczwtYyhSU1NfTW9kMyxSU0VfTW9kMyxNU0VfTW9kMyxSZWxhdGl2ZUVycm9yX01vZDMpCmBgYAoKTm93LCBhIG1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmU6CmBgYHtyfQp0LnRyYWluaW5nIDwtIHQudHJhaW5pbmcgJT4lIHNlbGVjdCgtVGVtcGVyYXR1cmVfbG9nX2tlbHZpbikKdC50ZXN0aW5nIDwtIHQudGVzdGluZyAlPiUgc2VsZWN0KC1UZW1wZXJhdHVyZV9sb2dfa2VsdmluKQpgYGAKCmBgYHtyfQpNb2Q0PC1sbShDYXNlc19taWxsaW9uX2xvZ34uLCBkYXRhPXQudHJhaW5pbmcpCnN1bW1hcnkoTW9kNCkKYGBgCgpQcmVkaWN0aW9uOgpgYGB7cn0KUHJlZDwtcHJlZGljdChNb2Q0LHQudGVzdGluZykKYGBgCgpFcnJvcnM6CmBgYHtyfQpSU1NfTW9kNDwtUlNTKFByZWQsdC50ZXN0aW5nJENhc2VzX21pbGxpb25fbG9nKQpSU0VfTW9kNDwtUlNFKFByZWQsdC50ZXN0aW5nJENhc2VzX21pbGxpb25fbG9nLGRpbSh0LnRlc3RpbmcpWzJdLTEpCk1TRV9Nb2Q0PC1NU0UoUHJlZCx0LnRlc3RpbmckQ2FzZXNfbWlsbGlvbl9sb2cpClJlbGF0aXZlRXJyb3JfTW9kNDwtUmVsYXRpdmVFcnJvcihQcmVkLHQudGVzdGluZyRDYXNlc19taWxsaW9uX2xvZykKCk1vZDRfRXJyb3JzPC1jKFJTU19Nb2Q0LFJTRV9Nb2Q0LE1TRV9Nb2Q0LFJlbGF0aXZlRXJyb3JfTW9kNCkKYGBgCgpDcmVhdGUgYSByYWRhcnBsb3Q6CmBgYHtyfQpFcnJvcnM8LXJiaW5kKE1vZDNfRXJyb3JzLE1vZDRfRXJyb3JzKQoKcm93bmFtZXMoRXJyb3JzKTwtYygiTW9kZWwgd2l0aCB0ZW1wZXJhdHVyZSIsIk1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmUiKQoKY29sbmFtZXMoRXJyb3JzKTwtYygiUmVzaWR1YWwgU3VtIG9mIFNxdWFyZSIsIlJlc2lkdWFsIFN0YW5kYXJkIEVycm9yIiwiTWVhbiBTcXVhcmVkIEVycm9yIiwiUmVsYXRpdmUgZXJyb3IiKQoKRXJyb3JzPC1hcy5kYXRhLmZyYW1lKEVycm9ycykKCm1heGltdW08LWFwcGx5KEVycm9ycywyLG1heCkKCm1pbmltdW08LWFwcGx5KEVycm9ycywyLG1pbikKCkVycm9yczwtcmJpbmQobWluaW11bSxFcnJvcnMpCgpFcnJvcnM8LXJiaW5kKG1heGltdW0sRXJyb3JzKQpgYGAKCmBgYHtyfQpyYWRhcmNoYXJ0KEVycm9ycyxtYXhtaW49VFJVRSxheGlzdHlwZT00LGF4aXNsYWJjb2w9InNsYXRlZ3JheTQiLAogICAgICAgICAgIGNlbnRlcnplcm89RkFMU0Usc2VnPTgsY2dsY29sPSJncmF5NjciLAogICAgICAgICAgIHBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpLAogICAgICAgICAgIHBsdHk9MSwKICAgICAgICAgICBwbHdkPTMsCiAgICAgICAgICAgdGl0bGU9IkVycm9yIGNvbXBhcmlzb24iKQoKbGVnZW5kIDwtbGVnZW5kKDEuNSwxLCBsZWdlbmQ9YygiV2l0aCB0ZW1wZXJhdHVyZSIsIldpdGhvdXQgdGVtcGVyYXR1cmUiKSwKICAgICAgICAgICAgICAgICBzZWcubGVuPS0xLjQsCiAgICAgICAgICAgICAgICAgdGl0bGU9IkVycm9ycyIsCiAgICAgICAgICAgICAgICAgcGNoPTIxLCAKICAgICAgICAgICAgICAgICBidHk9Im4iICxsd2Q9MywgeS5pbnRlcnNwPTEsIGhvcml6PUZBTFNFLAogICAgICAgICAgICAgICAgIGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIikpCmBgYAoKIyBGb3JlY2FzdCBieSBjb3VudHJ5CgojIyBSZXB1YmxpYyBvZiBDb3N0YSBSaWNhCgohW10oQ29zdGFSaWNhRmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9CkNvc3RhUmljYV9UZW1wPC1yZWFkLmNzdigiQ29zdGFSaWNhX1RlbXBlcmF0dXJlLmNzdiIsc2VwPSI7IikKQ29zdGFSaWNhX1RlbXAkRGF0ZTwtYXMuRGF0ZShDb3N0YVJpY2FfVGVtcCREYXRlLGZvcm1hdD0iJWQvJW0vJVkiKQpgYGAKCmBgYHtyfQpDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcDwtQ09WSURfMl9EYXlfQ29zdGFSaWNhICU+JQogIGlubmVyX2pvaW4oQ29zdGFSaWNhX1RlbXAsYnk9YygiRGF0ZTIiPSJEYXRlIikpCgpDT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wPC14dHMoQ09WSURfMl9EYXlfQ29zdGFSaWNhX1RlbXAkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCREYXRlMikKYGBgCgpgYGB7cn0KQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1RyYWluPC1DT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wWzE6NjRdICNVbnRpbCBNYXJjaCAyNXRoCkNPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wWzY1Omxlbmd0aChDT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wKV0gI0Zyb20gTWFyY2ggMjZ0aCBvbndhcmRzCmBgYAoKR2V0IHRoZSBsYWdnZWQgZGlmZmVyZW5jZToKYGBge3J9CnBsb3QoZGlmZihDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVHJhaW4pLHR5cGU9ImwiLG1haW49IjFzdCAiKSAKYGBgCgpDb3JyZWxvZ3JhbXMgb2YgZmlyc3QgZGlmZXJlbmNlOgpgYGB7cn0KdHNkaXNwbGF5KGRpZmYoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMSwyLDEpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgQ29zdGEgUmljYToKCkFSSU1BMV9Db3N0YVJpY2E8LWF1dG8uYXJpbWEoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCRUZW1wZXJhdHVyZV9Db3N0YVJpY2FbMTo2NF0pCnN1bW1hcnkoQVJJTUExX0Nvc3RhUmljYSkKYGBgCgpQcmVkaWN0aXZlIGVycm9yIGZ1bmN0aW9uczoKYGBge3J9CiNSZWxhdGl2ZSBlcnJvcgpSRSA8LSBmdW5jdGlvbihGb3JlLEFjdHVhbCkgewogIHJldHVybihzdW0oYWJzKEZvcmUtQWN0dWFsKSkvYWJzKHN1bShBY3R1YWwpKSkKfQoKCiNNQVBFCk1BUEU8LWZ1bmN0aW9uKEZvcmUsQWN0dWFsKXsKICByZXR1cm4oCiAgICBtZWFuKGFicyhBY3R1YWwtRm9yZSkvYWJzKEFjdHVhbCkpKjEwMAogICAgKQp9CgojIG1lYW4gc3F1YXJlZCBlcnJvciAoTVNFKQpNU0U8LWZ1bmN0aW9uKEZvcmUsQWN0dWFsKSB7CiAgTjwtbGVuZ3RoKEFjdHVhbCkKICBzczwtc3VtKChBY3R1YWwtRm9yZSleMikKICByZXR1cm4oKDEvTikqc3MpCn0KCiNQRkEKUEZBIDwtIGZ1bmN0aW9uKEZvcmUsQWN0dWFsKSB7CiAgVG90YWw8LTAKICBOPC1sZW5ndGgoRm9yZSkKICBmb3IoaSBpbiAxOk4pIHsKICAgIGlmKEZvcmVbaV0+PUFjdHVhbFtpXSkKICAgICAgVG90YWw8LVRvdGFsKzEgICAgICAKICB9CiAgcmV0dXJuKFRvdGFsL04pCn0KYGBgCgpGb3JlY2FzdCBwcmVkaWN0aW9uIHRvIGNvbXBhcmUKYGBge3J9ClRlc3RfQ29zdGFSaWNhX1RlbXA8LUNPVklEXzJfRGF5X0Nvc3RhUmljYV9UZW1wJFRlbXBlcmF0dXJlX0Nvc3RhUmljYVs2NTpsZW5ndGgoQ09WSURfRGF5X3Nlcmllc19Db3N0YVJpY2FfVGVtcCldCgoKUF9DUklfMTwtZm9yZWNhc3QoQVJJTUExX0Nvc3RhUmljYSx4cmVnID0gVGVzdF9Db3N0YVJpY2FfVGVtcCkKYGBgCgpDYWxjdWxhdGUgZXJyb3JzOgpgYGB7cn0KUkVfQ1JJXzE8LVJFKFBfQ1JJXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpKQpNQVBFX0NSSV8xPC1NQVBFKFBfQ1JJXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpKQpNU0VfQ1JJXzE8LU1TRShQX0NSSV8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0KSkKUEZBX0NSSV8xPC1QRkEoUF9DUklfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVGVzdCkpCgpFcnJvcnMuQ1JJXzE8LWMoUkVfQ1JJXzEsTUFQRV9DUklfMSxNU0VfQ1JJXzEsUEZBX0NSSV8xKQpFcnJvcnMuQ1JJXzEKYGBgCgpNYW51YWwgTUFQRToKYGBge3J9CmRkPC1kYXRhLmZyYW1lKEFjdHVhbD1hcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpLAogICAgICAgICAgICAgICBGb3JlPVBfQ1JJXzEkbWVhbgopCmRkPC1kZCAlPiUgbXV0YXRlKEFic19QZXJfRXJyb3I9YWJzKEFjdHVhbC1Gb3JlKS9hYnMoQWN0dWFsKSkKbWVhbihkZCRBYnNfUGVyX0Vycm9yKSoxMDAKYGBgCgoqTW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZSoKCkFSSU1BKDEsMiwwKQpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIENvc3RhIFJpY2E6CgpBUklNQTJfQ29zdGFSaWNhPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UcmFpbikKc3VtbWFyeShBUklNQTJfQ29zdGFSaWNhKQpgYGAKCkZvcmVjYXN0IHByZWRpY3Rpb24gdG8gY29tcGFyZQpgYGB7cn0KUF9DUklfMjwtZm9yZWNhc3QoQVJJTUEyX0Nvc3RhUmljYSxoPWxlbmd0aChDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVGVzdCkpCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0NSSV8yPC1SRShQX0NSSV8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0KSkKTUFQRV9DUklfMjwtTUFQRShQX0NSSV8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db3N0YVJpY2FfVGVtcF9UZXN0KSkKTVNFX0NSSV8yPC1NU0UoUF9DUklfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ29zdGFSaWNhX1RlbXBfVGVzdCkpClBGQV9DUklfMjwtUEZBKFBfQ1JJXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0Nvc3RhUmljYV9UZW1wX1Rlc3QpKQoKRXJyb3JzLkNSSV8yPC1jKFJFX0NSSV8yLE1BUEVfQ1JJXzIsTVNFX0NSSV8yLFBGQV9DUklfMikKRXJyb3JzLkNSSV8yCmBgYAoKYGBge3J9CkVycm9yczwtcmJpbmQoRXJyb3JzLkNSSV8xLEVycm9ycy5DUklfMikKCnJvd25hbWVzKEVycm9ycyk8LWMoIkFSSU1BWCgxLDIsMSkiLCJBUklNQSgxLDIsMCkiKQoKY29sbmFtZXMoRXJyb3JzKTwtYygiUmVsYXRpdmUgZXJyb3IiLCJNZWFuIEFicyAlIGVycm9yIiwiTWVhbiBTcXVhcmVkIEVycm9yIiwiUEZBIikKCkVycm9yczwtYXMuZGF0YS5mcmFtZShFcnJvcnMpCgptYXhpbXVtPC1hcHBseShFcnJvcnMsMixtYXgpCgptaW5pbXVtPC1hcHBseShFcnJvcnMsMixtaW4pCgpFcnJvcnM8LXJiaW5kKG1pbmltdW0sRXJyb3JzKQoKRXJyb3JzPC1yYmluZChtYXhpbXVtLEVycm9ycykKYGBgCgpgYGB7cn0KcmFkYXJjaGFydChFcnJvcnMsbWF4bWluPVRSVUUsYXhpc3R5cGU9NCxheGlzbGFiY29sPSJzbGF0ZWdyYXk0IiwKICAgICAgICAgICBjZW50ZXJ6ZXJvPUZBTFNFLHNlZz04LGNnbGNvbD0iZ3JheTY3IiwKICAgICAgICAgICBwY29sPWMoImRvZGdlcmJsdWUyIiwiZmlyZWJyaWNrMiIsImRhcmtvcmFuZ2UyIiwiZGFya29yY2hpZDIiKSwKICAgICAgICAgICBwbHR5PTEsCiAgICAgICAgICAgcGx3ZD0zLAogICAgICAgICAgIHRpdGxlPSJFcnJvciBjb21wYXJpc29uIikKCmxlZ2VuZCA8LWxlZ2VuZCgxLjUsMSwgbGVnZW5kPWMoIkFSSU1BWCgxLDIsMSkiLCJBUklNQSgxLDIsMCkiKSwKICAgICAgICAgICAgICAgICBzZWcubGVuPS0xLjQsCiAgICAgICAgICAgICAgICAgdGl0bGU9IkVycm9ycyIsCiAgICAgICAgICAgICAgICAgcGNoPTIxLCAKICAgICAgICAgICAgICAgICBidHk9Im4iICxsd2Q9MywgeS5pbnRlcnNwPTEsIGhvcml6PUZBTFNFLAogICAgICAgICAgICAgICAgIGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIikpCmBgYAoKQ29uY2x1c2lvbjogS2VlcCBtb2RlbCB3aXRoIHRlbXBlcmF0dXJlCgpNYWtlIG1vZGVsIHdpdGggdGhlIG92ZXJhbGwgc2VyaWVzCmBgYHtyfQojQXV0byBBcmltYSBmb3IgQ29zdGEgUmljYToKCkFSSU1BX0ZpbmFsX0Nvc3RhUmljYTwtYXV0by5hcmltYShDT1ZJRF9EYXlfc2VyaWVzX0Nvc3RhUmljYV9UZW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCRUZW1wZXJhdHVyZV9Db3N0YVJpY2EpCnN1bW1hcnkoQVJJTUFfRmluYWxfQ29zdGFSaWNhKQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX0Nvc3RhUmljYV9UZW1wPC1Db3N0YVJpY2FfVGVtcCRUZW1wZXJhdHVyZV9Db3N0YVJpY2FbQ29zdGFSaWNhX1RlbXAkRGF0ZT5tYXgoQ09WSURfMiREYXRlMildCgpQX0NSSV9GaW5hbDwtZm9yZWNhc3QoQVJJTUFfRmluYWxfQ29zdGFSaWNhLHhyZWcgPSBGdXR1cmVfQ29zdGFSaWNhX1RlbXApCkxvd19saW1fQ1JJPC1kYXRhLmZyYW1lKFBfQ1JJX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9DUkk8LWRhdGEuZnJhbWUoUF9DUklfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCREYXRlMikpCnBlcl8yIDwtIHNlcShhcy5EYXRlKG1heChDT1ZJRF8yX0RheV9Db3N0YVJpY2FfVGVtcCREYXRlMikrMSxmb3JtYXQ9IiVZLSVtLSVkIiksIGFzLkRhdGUoIjIwMjAtMDQtMzAiLGZvcm1hdD0iJVktJW0tJWQiKSwiMSBkYXkiKQoKCiMgTWVyZ2UgZm9yZWNhc3QgKyBhY3R1YWxzCmRhdGEgPC0geHRzKENPVklEX0RheV9zZXJpZXNfQ29zdGFSaWNhX1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX0NSSSA8LSB4dHMoTG93X2xpbV9DUkksb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X0NSSSA8LSB4dHMoUF9DUklfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9DUkkgPC0geHRzKFVwcF9saW1fQ1JJLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfQ1JJKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fQ1JJLCBGb3JlY2FzdF9DUkksIFVwcF9saW1fQ1JJKQoKYWxsX3Nlcmllc19DUkkgPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19DUkkpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19DUkksIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgQ29zdGEgUmljYSBjYXNlcyIseGxhYj0iRGF0ZSIsIHlsYWI9Ik5vdmVsIGNvcm9uYXZpcnVzIGNhc2VzIix3aWR0aCA9IDc1MCklPiUKICBkeVNlcmllcyhjKCdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpLGxhYmVsPSJGb3JlY2FzdCIsc3Ryb2tlV2lkdGg9MiwKICAgICAgICAgICBkcmF3UG9pbnRzID0gVFJVRSwgcG9pbnRTaXplID0gMiwgY29sb3I9cmdiKDE4OS8yNTUsNDQvMjU1LDQ3LzI1NSkpICU+JQogIGR5U2VyaWVzKCJBY3R1YWwiLGRyYXdQb2ludHMgPSBUUlVFLCBzdHJva2VXaWR0aD0yLCBwb2ludFNpemUgPSAyLAogICAgICAgICAgIGNvbG9yPXJnYigxMC8yNTUsNDQvMjU1LDExOS8yNTUpKSAlPiUgCiAgZHlSYW5nZVNlbGVjdG9yKCkKCmBgYAoKIyMgTWV4aWNvCgohW10oTWV4aWNvRmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9Ck1leGljb19UZW1wPC1yZWFkLmNzdigiTWV4aWNvX1RlbXBlcmF0dXJlLmNzdiIsc2VwPSI7IikKTWV4aWNvX1RlbXAkRGF0ZTwtYXMuRGF0ZShNZXhpY29fVGVtcCREYXRlLGZvcm1hdD0iJWQvJW0vJVkiKQpgYGAKCmBgYHtyfQpDT1ZJRF8yX0RheV9NZXhpY288LSBDT1ZJRF8yICU+JSAKICBmaWx0ZXIoQ291bnRyeS5SZWdpb24gJWluJSBjKCJNZXhpY28iKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X01leGljb19UZW1wPC1DT1ZJRF8yX0RheV9NZXhpY28gJT4lCiAgaW5uZXJfam9pbihNZXhpY29fVGVtcCxieT1jKCJEYXRlMiI9IkRhdGUiKSkKCkNPVklEX0RheV9zZXJpZXNfTWV4aWNvX1RlbXA8LXh0cyhDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X01leGljb19UZW1wJERhdGUyKQpgYGAKCmBgYHtyfQpDT1ZJRF9zZXJpZXNfTWV4aWNvX1RyYWluPC1DT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wWzE6NzRdICNVbnRpbCBBcHJpbCA0dGgKQ09WSURfc2VyaWVzX01leGljb19UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wWzc1Omxlbmd0aChDT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wKV0gI0Zyb20gQXByaWwgNXRoIG9ud2FyZHMKYGBgCgpHZXQgdGhlIGxhZ2dlZCBkaWZmZXJlbmNlOgpgYGB7cn0KcGxvdChkaWZmKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4pLHR5cGU9ImwiLG1haW49IjFzdCAiKSAKYGBgCgpDb3JyZWxvZ3JhbXMgb2YgZmlyc3QgZGlmZXJlbmNlOgpgYGB7cn0KdHNkaXNwbGF5KGRpZmYoQ09WSURfc2VyaWVzX01leGljb19UcmFpbikpCmBgYAoKQXJpbWEgbW9kZWw6IEFSSU1BKDIsMiwzKQpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIE1leGljbzoKCiNBUklNQTFfTWV4aWNvPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI3hyZWcgPSBDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCkFSSU1BMV9NZXhpY288LUFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sb3JkZXI9YygyLDIsMykseHJlZz1DT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCnN1bW1hcnkoQVJJTUExX01leGljbykKYGBgCgpGb3JlY2FzdCBwcmVkaWN0aW9uIHRvIGNvbXBhcmUKYGBge3J9ClRlc3RfTWV4aWNvX1RlbXA8LUNPVklEXzJfRGF5X01leGljb19UZW1wJFRlbXBlcmF0dXJlX01leGljb1s3NTpsZW5ndGgoQ09WSURfRGF5X3Nlcmllc19NZXhpY29fVGVtcCldCgoKUF9NRVhfMTwtZm9yZWNhc3QoQVJJTUExX01leGljbyx4cmVnID0gVGVzdF9NZXhpY29fVGVtcCkKYGBgCgpDYWxjdWxhdGUgZXJyb3JzOgpgYGB7cn0KUkVfTUVYXzE8LVJFKFBfTUVYXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX01leGljb19UZXN0KSkKTUFQRV9NRVhfMTwtTUFQRShQX01FWF8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpCk1TRV9NRVhfMTwtTVNFKFBfTUVYXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX01leGljb19UZXN0KSkKUEZBX01FWF8xPC1QRkEoUF9NRVhfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTWV4aWNvX1Rlc3QpKQoKRXJyb3JzLk1FWF8xPC1jKFJFX01FWF8xLE1BUEVfTUVYXzEsTVNFX01FWF8xLFBGQV9NRVhfMSkKRXJyb3JzLk1FWF8xCmBgYAoKTWFudWFsIE1BUEU6CmBgYHtyfQpkZDwtZGF0YS5mcmFtZShBY3R1YWw9YXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCksCiAgICAgICAgICAgICAgIEZvcmU9UF9NRVhfMSRtZWFuCikKZGQ8LWRkICU+JSBtdXRhdGUoQWJzX1Blcl9FcnJvcj1hYnMoQWN0dWFsLUZvcmUpL2FicyhBY3R1YWwpKQptZWFuKGRkJEFic19QZXJfRXJyb3IpKjEwMApgYGAKCipNb2RlbCB3aXRob3V0IHRlbXBlcmF0dXJlKgoKQVJJTUEoMiwyLDQpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgTWV4aWNvOgoKQVJJTUEyX01leGljbzwtYXV0by5hcmltYShDT1ZJRF9zZXJpZXNfTWV4aWNvX1RyYWluKQpzdW1tYXJ5KEFSSU1BMl9NZXhpY28pCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpQX01FWF8yPC1mb3JlY2FzdChBUklNQTJfTWV4aWNvLGg9bGVuZ3RoKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX01FWF8yPC1SRShQX01FWF8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpCk1BUEVfTUVYXzI8LU1BUEUoUF9NRVhfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTWV4aWNvX1Rlc3QpKQpNU0VfTUVYXzI8LU1TRShQX01FWF8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19NZXhpY29fVGVzdCkpClBGQV9NRVhfMjwtUEZBKFBfTUVYXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX01leGljb19UZXN0KSkKCkVycm9ycy5NRVhfMjwtYyhSRV9NRVhfMixNQVBFX01FWF8yLE1TRV9NRVhfMixQRkFfTUVYXzIpCkVycm9ycy5NRVhfMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5NRVhfMSxFcnJvcnMuTUVYXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMiwyLDMpIiwiQVJJTUEoMiwyLDQpIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMiwyLDMpIiwiQVJJTUEoMiwyLDQpIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIE1leGljbzoKCkFSSU1BX0ZpbmFsX01leGljbzwtYXV0by5hcmltYShDT1ZJRF9EYXlfc2VyaWVzX01leGljb19UZW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY28pCnN1bW1hcnkoQVJJTUFfRmluYWxfTWV4aWNvKQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX01leGljb19UZW1wPC1NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bTWV4aWNvX1RlbXAkRGF0ZT5tYXgoQ09WSURfMiREYXRlMildCgpQX01FWF9GaW5hbDwtZm9yZWNhc3QoQVJJTUFfRmluYWxfTWV4aWNvLHhyZWcgPSBGdXR1cmVfTWV4aWNvX1RlbXApCkxvd19saW1fTUVYPC1kYXRhLmZyYW1lKFBfTUVYX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9NRVg8LWRhdGEuZnJhbWUoUF9NRVhfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCREYXRlMikpCnBlcl8yIDwtIHNlcShhcy5EYXRlKG1heChDT1ZJRF8yX0RheV9NZXhpY29fVGVtcCREYXRlMikrMSxmb3JtYXQ9IiVZLSVtLSVkIiksIGFzLkRhdGUoIjIwMjAtMDQtMzAiLGZvcm1hdD0iJVktJW0tJWQiKSwiMSBkYXkiKQoKCiMgTWVyZ2UgZm9yZWNhc3QgKyBhY3R1YWxzCmRhdGEgPC0geHRzKENPVklEX0RheV9zZXJpZXNfTWV4aWNvX1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX01FWCA8LSB4dHMoTG93X2xpbV9NRVgsb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X01FWCA8LSB4dHMoUF9NRVhfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9NRVggPC0geHRzKFVwcF9saW1fTUVYLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfTUVYKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fTUVYLCBGb3JlY2FzdF9NRVgsIFVwcF9saW1fTUVYKQoKYWxsX3Nlcmllc19NRVggPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19NRVgpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19NRVgsIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgTWV4aWNvIGNhc2VzIix4bGFiPSJEYXRlIiwgeWxhYj0iTm92ZWwgY29yb25hdmlydXMgY2FzZXMiLHdpZHRoID0gNzUwKSU+JQogIGR5U2VyaWVzKGMoJ0xvd2VyX2xpbWl0JywgJ0ZvcmVjYXN0JywgJ1VwcGVyX2xpbWl0JyksbGFiZWw9IkZvcmVjYXN0IixzdHJva2VXaWR0aD0yLAogICAgICAgICAgIGRyYXdQb2ludHMgPSBUUlVFLCBwb2ludFNpemUgPSAyLCBjb2xvcj1yZ2IoMTg5LzI1NSw0NC8yNTUsNDcvMjU1KSkgJT4lCiAgZHlTZXJpZXMoIkFjdHVhbCIsZHJhd1BvaW50cyA9IFRSVUUsIHN0cm9rZVdpZHRoPTIsIHBvaW50U2l6ZSA9IDIsCiAgICAgICAgICAgY29sb3I9cmdiKDQzLzI1NSwxMDIvMjU1LDczLzI1NSkpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoKQoKYGBgCgojIyBJdGFseQoKIVtdKEl0YWx5RmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9Ckl0YWx5X1RlbXA8LXJlYWQuY3N2KCJJdGFseV9UZW1wZXJhdHVyZS5jc3YiLHNlcD0iOyIpCkl0YWx5X1RlbXAkRGF0ZTwtYXMuRGF0ZShJdGFseV9UZW1wJERhdGUsZm9ybWF0PSIlZC8lbS8lWSIpCmBgYAoKYGBge3J9CkNPVklEXzJfRGF5X0l0YWx5PC0gQ09WSURfMiAlPiUgCiAgZmlsdGVyKENvdW50cnkuUmVnaW9uICVpbiUgYygiSXRhbHkiKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X0l0YWx5X1RlbXA8LUNPVklEXzJfRGF5X0l0YWx5ICU+JQogIGlubmVyX2pvaW4oSXRhbHlfVGVtcCxieT1jKCJEYXRlMiI9IkRhdGUiKSkKCkNPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVtcDwteHRzKENPVklEXzJfRGF5X0l0YWx5X1RlbXAkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9JdGFseV9UZW1wJERhdGUyKQpgYGAKCmBgYHtyfQpDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RyYWluPC1DT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXBbMTo2NF0gI1VudGlsIE1hcmNoIDI1dGgKQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXBbNjU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVtcCldICNGcm9tIE1hcmNoIDI2dGggb253YXJkcwpgYGAKCkdldCB0aGUgbGFnZ2VkIGRpZmZlcmVuY2U6CmBgYHtyfQpwbG90KGRpZmYoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UcmFpbiksdHlwZT0ibCIsbWFpbj0iMXN0ICIpIApgYGAKCkNvcnJlbG9ncmFtcyBvZiBmaXJzdCBkaWZlcmVuY2U6CmBgYHtyfQp0c2Rpc3BsYXkoZGlmZihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMSwyLDApCmBgYHtyfQojQXV0byBBcmltYSBmb3IgSXRhbHk6CgpBUklNQTFfSXRhbHk8LWF1dG8uYXJpbWEoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UcmFpbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4cmVnID0gQ09WSURfMl9EYXlfSXRhbHlfVGVtcCRUZW1wZXJhdHVyZV9JdGFseVsxOjY0XSkKI0FSSU1BMV9NZXhpY288LUFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sb3JkZXI9YygyLDIsMykseHJlZz1DT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCnN1bW1hcnkoQVJJTUExX0l0YWx5KQpgYGAKCkZvcmVjYXN0IHByZWRpY3Rpb24gdG8gY29tcGFyZQpgYGB7cn0KVGVzdF9JdGFseV9UZW1wPC1DT1ZJRF8yX0RheV9JdGFseV9UZW1wJFRlbXBlcmF0dXJlX0l0YWx5WzY1Omxlbmd0aChDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXApXQoKClBfSVRBXzE8LWZvcmVjYXN0KEFSSU1BMV9JdGFseSx4cmVnID0gVGVzdF9JdGFseV9UZW1wKQpgYGAKCkNhbGN1bGF0ZSBlcnJvcnM6CmBgYHtyfQpSRV9JVEFfMTwtUkUoUF9JVEFfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1Rlc3QpKQpNQVBFX0lUQV8xPC1NQVBFKFBfSVRBXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0KSkKTVNFX0lUQV8xPC1NU0UoUF9JVEFfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1Rlc3QpKQpQRkFfSVRBXzE8LVBGQShQX0lUQV8xJG1lYW4sYXMudmVjdG9yKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpCgpFcnJvcnMuSVRBXzE8LWMoUkVfSVRBXzEsTUFQRV9JVEFfMSxNU0VfSVRBXzEsUEZBX0lUQV8xKQpFcnJvcnMuSVRBXzEKYGBgCgpNYW51YWwgTUFQRToKYGBge3J9CmRkPC1kYXRhLmZyYW1lKEFjdHVhbD1hcy52ZWN0b3IoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0KSwKICAgICAgICAgICAgICAgRm9yZT1QX0lUQV8xJG1lYW4KKQpkZDwtZGQgJT4lIG11dGF0ZShBYnNfUGVyX0Vycm9yPWFicyhBY3R1YWwtRm9yZSkvYWJzKEFjdHVhbCkpCm1lYW4oZGQkQWJzX1Blcl9FcnJvcikqMTAwCmBgYAoKKk1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmUqCgpBUklNQSgxLDIsMCkKYGBge3J9CiNBdXRvIEFyaW1hIGZvciBJdGFseToKQVJJTUEyX0l0YWx5PC1hdXRvLmFyaW1hKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVHJhaW4pCnN1bW1hcnkoQVJJTUEyX0l0YWx5KQpgYGAKCkZvcmVjYXN0IHByZWRpY3Rpb24gdG8gY29tcGFyZQpgYGB7cn0KUF9JVEFfMjwtZm9yZWNhc3QoQVJJTUEyX0l0YWx5LGg9bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0lUQV8yPC1SRShQX0lUQV8yJG1lYW4sYXMudmVjdG9yKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpCk1BUEVfSVRBXzI8LU1BUEUoUF9JVEFfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1Rlc3QpKQpNU0VfSVRBXzI8LU1TRShQX0lUQV8yJG1lYW4sYXMudmVjdG9yKENPVklEX0RheV9zZXJpZXNfSXRhbHlfVGVzdCkpClBGQV9JVEFfMjwtUEZBKFBfSVRBXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZXN0KSkKCkVycm9ycy5JVEFfMjwtYyhSRV9JVEFfMixNQVBFX0lUQV8yLE1TRV9JVEFfMixQRkFfSVRBXzIpCkVycm9ycy5JVEFfMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5JVEFfMSxFcnJvcnMuSVRBXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMSwyLDApIiwiQVJJTUEoMSwyLDApIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMSwyLDApIiwiQVJJTUEoMSwyLDApIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIEl0YWx5OgoKQVJJTUFfRmluYWxfSXRhbHk8LWF1dG8uYXJpbWEoQ09WSURfRGF5X3Nlcmllc19JdGFseV9UZW1wKQpzdW1tYXJ5KEFSSU1BX0ZpbmFsX0l0YWx5KQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX0l0YWx5X1RlbXA8LUl0YWx5X1RlbXAkVGVtcGVyYXR1cmVfSXRhbHlbSXRhbHlfVGVtcCREYXRlPm1heChDT1ZJRF8yJERhdGUyKV0KClBfSVRBX0ZpbmFsPC1mb3JlY2FzdChBUklNQV9GaW5hbF9JdGFseSxoPWxlbmd0aChGdXR1cmVfSXRhbHlfVGVtcCkpCkxvd19saW1fSVRBPC1kYXRhLmZyYW1lKFBfSVRBX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9JVEE8LWRhdGEuZnJhbWUoUF9JVEFfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9JdGFseV9UZW1wJERhdGUyKSkKcGVyXzIgPC0gc2VxKGFzLkRhdGUobWF4KENPVklEXzJfRGF5X0l0YWx5X1RlbXAkRGF0ZTIpKzEsZm9ybWF0PSIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDIwLTA0LTMwIixmb3JtYXQ9IiVZLSVtLSVkIiksIjEgZGF5IikKCgojIE1lcmdlIGZvcmVjYXN0ICsgYWN0dWFscwpkYXRhIDwtIHh0cyhDT1ZJRF9EYXlfc2VyaWVzX0l0YWx5X1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX0lUQSA8LSB4dHMoTG93X2xpbV9JVEEsb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X0lUQSA8LSB4dHMoUF9JVEFfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9JVEEgPC0geHRzKFVwcF9saW1fSVRBLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfSVRBKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fSVRBLCBGb3JlY2FzdF9JVEEsIFVwcF9saW1fSVRBKQoKYWxsX3Nlcmllc19JVEEgPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19JVEEpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19JVEEsIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgSXRhbHkgY2FzZXMiLHhsYWI9IkRhdGUiLCB5bGFiPSJOb3ZlbCBjb3JvbmF2aXJ1cyBjYXNlcyIsd2lkdGggPSA3NTApJT4lCiAgZHlTZXJpZXMoYygnTG93ZXJfbGltaXQnLCAnRm9yZWNhc3QnLCAnVXBwZXJfbGltaXQnKSxsYWJlbD0iRm9yZWNhc3QiLHN0cm9rZVdpZHRoPTIsCiAgICAgICAgICAgZHJhd1BvaW50cyA9IFRSVUUsIHBvaW50U2l6ZSA9IDIsIGNvbG9yPXJnYigxOTAvMjU1LDU5LzI1NSw2MS8yNTUpKSAlPiUKICBkeVNlcmllcygiQWN0dWFsIixkcmF3UG9pbnRzID0gVFJVRSwgc3Ryb2tlV2lkdGg9MiwgcG9pbnRTaXplID0gMiwKICAgICAgICAgICBjb2xvcj1yZ2IoNjQvMjU1LDE0My8yNTUsNzgvMjU1KSkgJT4lIAogIGR5UmFuZ2VTZWxlY3RvcigpCgpgYGAKCgojIyBMZWJhbm9uCgohW10oTGViYW5vbkZsYWcucG5nKXt3aWR0aD00MCV9CgoqTW9kZWwgd2l0aCB0ZW1wZXJhdHVyZSoKCmBgYHtyfQpMZWJhbm9uX1RlbXA8LXJlYWQuY3N2KCJMZWJhbm9uX1RlbXBlcmF0dXJlLmNzdiIsc2VwPSI7IikKTGViYW5vbl9UZW1wJERhdGU8LWFzLkRhdGUoTGViYW5vbl9UZW1wJERhdGUsZm9ybWF0PSIlZC8lbS8lWSIpCmBgYAoKYGBge3J9CkNPVklEXzJfRGF5X0xlYmFub248LSBDT1ZJRF8yICU+JSAKICBmaWx0ZXIoQ291bnRyeS5SZWdpb24gJWluJSBjKCJMZWJhbm9uIikpICU+JSAKICBncm91cF9ieShEYXRlMikgJT4lIHN1bW1hcmlzZShXb3JsZF9jb25maXJtZWQ9c3VtKENvbmZpcm1lZCkpCgpDT1ZJRF8yX0RheV9MZWJhbm9uX1RlbXA8LUNPVklEXzJfRGF5X0xlYmFub24gJT4lCiAgaW5uZXJfam9pbihMZWJhbm9uX1RlbXAsYnk9YygiRGF0ZTIiPSJEYXRlIikpCgpDT1ZJRF9EYXlfc2VyaWVzX0xlYmFub25fVGVtcDwteHRzKENPVklEXzJfRGF5X0xlYmFub25fVGVtcCRXb3JsZF9jb25maXJtZWQsIG9yZGVyLmJ5PUNPVklEXzJfRGF5X0xlYmFub25fVGVtcCREYXRlMikKYGBgCgpgYGB7cn0KQ09WSURfc2VyaWVzX0xlYmFub25fVHJhaW48LUNPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wWzE6NzRdICNVbnRpbCBBcHJpbCA0dGgKQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdDwtQ09WSURfRGF5X3Nlcmllc19MZWJhbm9uX1RlbXBbNzU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wKV0gI0Zyb20gQXByaWwgNHRoIG9ud2FyZHMKYGBgCgpHZXQgdGhlIGxhZ2dlZCBkaWZmZXJlbmNlOgpgYGB7cn0KcGxvdChkaWZmKENPVklEX3Nlcmllc19MZWJhbm9uX1RyYWluKSx0eXBlPSJsIixtYWluPSIxc3QgIikgCmBgYAoKQ29ycmVsb2dyYW1zIG9mIGZpcnN0IGRpZmVyZW5jZToKYGBge3J9CnRzZGlzcGxheShkaWZmKENPVklEX3Nlcmllc19MZWJhbm9uX1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMSwxLDIpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgTGViYW5vbjoKCkFSSU1BMV9MZWJhbm9uPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19MZWJhbm9uX1RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9MZWJhbm9uX1RlbXAkVGVtcGVyYXR1cmVfTGViYW5vblsxOjc0XSkKI0FSSU1BMV9NZXhpY288LUFyaW1hKENPVklEX3Nlcmllc19NZXhpY29fVHJhaW4sb3JkZXI9YygyLDIsMykseHJlZz1DT1ZJRF8yX0RheV9NZXhpY29fVGVtcCRUZW1wZXJhdHVyZV9NZXhpY29bMTo3NF0pCnN1bW1hcnkoQVJJTUExX0xlYmFub24pCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpUZXN0X0xlYmFub25fVGVtcDwtQ09WSURfMl9EYXlfTGViYW5vbl9UZW1wJFRlbXBlcmF0dXJlX0xlYmFub25bNzU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wKV0KCgpQX0xCTl8xPC1mb3JlY2FzdChBUklNQTFfTGViYW5vbix4cmVnID0gVGVzdF9MZWJhbm9uX1RlbXApCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0xCTl8xPC1SRShQX0xCTl8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19MZWJhbm9uX1Rlc3QpKQpNQVBFX0xCTl8xPC1NQVBFKFBfTEJOXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpCk1TRV9MQk5fMTwtTVNFKFBfTEJOXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpClBGQV9MQk5fMTwtUEZBKFBfTEJOXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpCgpFcnJvcnMuTEJOXzE8LWMoUkVfTEJOXzEsTUFQRV9MQk5fMSxNU0VfTEJOXzEsUEZBX0xCTl8xKQpFcnJvcnMuTEJOXzEKYGBgCgpNYW51YWwgTUFQRToKYGBge3J9CmRkPC1kYXRhLmZyYW1lKEFjdHVhbD1hcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCksCiAgICAgICAgICAgICAgIEZvcmU9UF9MQk5fMSRtZWFuCikKZGQ8LWRkICU+JSBtdXRhdGUoQWJzX1Blcl9FcnJvcj1hYnMoQWN0dWFsLUZvcmUpL2FicyhBY3R1YWwpKQptZWFuKGRkJEFic19QZXJfRXJyb3IpKjEwMApgYGAKCipNb2RlbCB3aXRob3V0IHRlbXBlcmF0dXJlKgoKQVJJTUEoMCwyLDIpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgTGViYW5vbjoKQVJJTUEyX0xlYmFub248LWF1dG8uYXJpbWEoQ09WSURfc2VyaWVzX0xlYmFub25fVHJhaW4pCnN1bW1hcnkoQVJJTUEyX0xlYmFub24pCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpQX0xCTl8yPC1mb3JlY2FzdChBUklNQTJfTGViYW5vbixoPWxlbmd0aChDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKYGBgCgpDYWxjdWxhdGUgZXJyb3JzOgpgYGB7cn0KUkVfTEJOXzI8LVJFKFBfTEJOXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0xlYmFub25fVGVzdCkpCk1BUEVfTEJOXzI8LU1BUEUoUF9MQk5fMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKTVNFX0xCTl8yPC1NU0UoUF9MQk5fMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKUEZBX0xCTl8yPC1QRkEoUF9MQk5fMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfTGViYW5vbl9UZXN0KSkKCkVycm9ycy5MQk5fMjwtYyhSRV9MQk5fMixNQVBFX0xCTl8yLE1TRV9MQk5fMixQRkFfTEJOXzIpCkVycm9ycy5MQk5fMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5MQk5fMSxFcnJvcnMuTEJOXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMSwxLDIpIiwiQVJJTUEoMCwyLDIpIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMSwxLDIpIiwiQVJJTUEoMCwyLDIpIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIExlYmFub246CgpBUklNQV9GaW5hbF9MZWJhbm9uPC1hdXRvLmFyaW1hKENPVklEX0RheV9zZXJpZXNfTGViYW5vbl9UZW1wKQpzdW1tYXJ5KEFSSU1BX0ZpbmFsX0xlYmFub24pCmBgYAoKRmluYWwgZm9yZWNhc3Q6CmBgYHtyfQpGdXR1cmVfTGViYW5vbl9UZW1wPC1MZWJhbm9uX1RlbXAkVGVtcGVyYXR1cmVfTGViYW5vbltMZWJhbm9uX1RlbXAkRGF0ZT5tYXgoQ09WSURfMiREYXRlMildCgpQX0xCTl9GaW5hbDwtZm9yZWNhc3QoQVJJTUFfRmluYWxfTGViYW5vbixoPWxlbmd0aChGdXR1cmVfTGViYW5vbl9UZW1wKSkKTG93X2xpbV9MQk48LWRhdGEuZnJhbWUoUF9MQk5fRmluYWwkbG93ZXIpWywyXQpVcHBfbGltX0xCTjwtZGF0YS5mcmFtZShQX0xCTl9GaW5hbCR1cHBlcilbLDJdCmBgYAoKRm9yIG1ha2luZyB0aGUgcGxvdDoKYGBge3J9CiMjRGF0YSBwZXJpb2RzCnBlcl8xIDwtIGFzLkRhdGUoYXMuY2hhcmFjdGVyKENPVklEXzJfRGF5X0xlYmFub25fVGVtcCREYXRlMikpCnBlcl8yIDwtIHNlcShhcy5EYXRlKG1heChDT1ZJRF8yX0RheV9MZWJhbm9uX1RlbXAkRGF0ZTIpKzEsZm9ybWF0PSIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDIwLTA0LTMwIixmb3JtYXQ9IiVZLSVtLSVkIiksIjEgZGF5IikKCgojIE1lcmdlIGZvcmVjYXN0ICsgYWN0dWFscwpkYXRhIDwtIHh0cyhDT1ZJRF9EYXlfc2VyaWVzX0xlYmFub25fVGVtcCxvcmRlci5ieT1wZXJfMSkgCmRhdGFOQSA8LSByZXAoTkEsIGxlbmd0aChkYXRhKSkKQSA8LSBjYmluZChkYXRhLGRhdGFOQSxkYXRhTkEsZGF0YU5BKQoKCkxvd19saW1fTEJOIDwtIHh0cyhMb3dfbGltX0xCTixvcmRlci5ieT1wZXJfMikKRm9yZWNhc3RfTEJOIDwtIHh0cyhQX0xCTl9GaW5hbCRtZWFuLG9yZGVyLmJ5PXBlcl8yKQpVcHBfbGltX0xCTiA8LSB4dHMoVXBwX2xpbV9MQk4sb3JkZXIuYnk9cGVyXzIpCnByZWROQSA8LSByZXAoTkEsIGxlbmd0aChGb3JlY2FzdF9JVEEpKQpCIDwtIGNiaW5kKHByZWROQSwgTG93X2xpbV9MQk4sIEZvcmVjYXN0X0xCTiwgVXBwX2xpbV9MQk4pCgphbGxfc2VyaWVzX0xCTiA8LSBkYXRhLmZyYW1lKHJiaW5kKGFzLm1hdHJpeChBKSxhcy5tYXRyaXgoQikpKQpjb2xuYW1lcyhhbGxfc2VyaWVzX0xCTikgPC0gYygnQWN0dWFsJywgJ0xvd2VyX2xpbWl0JywgJ0ZvcmVjYXN0JywgJ1VwcGVyX2xpbWl0JykKYGBgCgpgYGB7cn0KZHlncmFwaChhbGxfc2VyaWVzX0xCTiwgbWFpbj0iU0FSUy1DT1YyLW91dGJyZWFrOiBUb3RhbCBMZWJhbm9uIGNhc2VzIix4bGFiPSJEYXRlIiwgeWxhYj0iTm92ZWwgY29yb25hdmlydXMgY2FzZXMiLHdpZHRoID0gNzUwKSU+JQogIGR5U2VyaWVzKGMoJ0xvd2VyX2xpbWl0JywgJ0ZvcmVjYXN0JywgJ1VwcGVyX2xpbWl0JyksbGFiZWw9IkZvcmVjYXN0IixzdHJva2VXaWR0aD0yLAogICAgICAgICAgIGRyYXdQb2ludHMgPSBUUlVFLCBwb2ludFNpemUgPSAyLCBjb2xvcj1yZ2IoMjE4LzI1NSw1NS8yNTUsNTAvMjU1KSkgJT4lCiAgZHlTZXJpZXMoIkFjdHVhbCIsZHJhd1BvaW50cyA9IFRSVUUsIHN0cm9rZVdpZHRoPTIsIHBvaW50U2l6ZSA9IDIsCiAgICAgICAgICAgY29sb3I9cmdiKDczLzI1NSwxNjMvMjU1LDkwLzI1NSkpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoKQoKYGBgCgojIyBDb2xvbWJpYQoKIVtdKENvbG9tYmlhRmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9CkNvbG9tYmlhX1RlbXA8LXJlYWQuY3N2KCJDb2xvbWJpYV9UZW1wZXJhdHVyZS5jc3YiLHNlcD0iOyIpCkNvbG9tYmlhX1RlbXAkRGF0ZTwtYXMuRGF0ZShDb2xvbWJpYV9UZW1wJERhdGUsZm9ybWF0PSIlZC8lbS8lWSIpCmBgYAoKYGBge3J9CkNPVklEXzJfRGF5X0NvbG9tYmlhPC0gQ09WSURfMiAlPiUgCiAgZmlsdGVyKENvdW50cnkuUmVnaW9uICVpbiUgYygiQ29sb21iaWEiKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X0NvbG9tYmlhX1RlbXA8LUNPVklEXzJfRGF5X0NvbG9tYmlhICU+JQogIGlubmVyX2pvaW4oQ29sb21iaWFfVGVtcCxieT1jKCJEYXRlMiI9IkRhdGUiKSkKCkNPVklEX0RheV9zZXJpZXNfQ29sb21iaWFfVGVtcDwteHRzKENPVklEXzJfRGF5X0NvbG9tYmlhX1RlbXAkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9Db2xvbWJpYV9UZW1wJERhdGUyKQpgYGAKCmBgYHtyfQpDT1ZJRF9zZXJpZXNfQ29sb21iaWFfVHJhaW48LUNPVklEX0RheV9zZXJpZXNfQ29sb21iaWFfVGVtcFsxOjc0XSAjVW50aWwgQXByaWwgNHRoCkNPVklEX3Nlcmllc19Db2xvbWJpYV9UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX0NvbG9tYmlhX1RlbXBbNzU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfQ29sb21iaWFfVGVtcCldICNGcm9tIEFwcmlsIDV0aCBvbndhcmRzCmBgYAoKR2V0IHRoZSBsYWdnZWQgZGlmZmVyZW5jZToKYGBge3J9CnBsb3QoZGlmZihDT1ZJRF9zZXJpZXNfQ29sb21iaWFfVHJhaW4pLHR5cGU9ImwiLG1haW49IjFzdCAiKSAKYGBgCgpDb3JyZWxvZ3JhbXMgb2YgZmlyc3QgZGlmZXJlbmNlOgpgYGB7cn0KdHNkaXNwbGF5KGRpZmYoQ09WSURfc2VyaWVzX0NvbG9tYmlhX1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMiwyLDIpCmBgYHtyfQojQXV0byBBcmltYSBmb3IgQ29sb21iaWE6CgpBUklNQTFfQ29sb21iaWE8LWF1dG8uYXJpbWEoQ09WSURfc2VyaWVzX0NvbG9tYmlhX1RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9Db2xvbWJpYV9UZW1wJFRlbXBlcmF0dXJlX0NvbG9tYmlhWzE6NzRdKQojQVJJTUExX01leGljbzwtQXJpbWEoQ09WSURfc2VyaWVzX01leGljb19UcmFpbixvcmRlcj1jKDIsMiwzKSx4cmVnPUNPVklEXzJfRGF5X01leGljb19UZW1wJFRlbXBlcmF0dXJlX01leGljb1sxOjc0XSkKc3VtbWFyeShBUklNQTFfQ29sb21iaWEpCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpUZXN0X0NvbG9tYmlhX1RlbXA8LUNPVklEXzJfRGF5X0NvbG9tYmlhX1RlbXAkVGVtcGVyYXR1cmVfQ29sb21iaWFbNzU6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfQ29sb21iaWFfVGVtcCldCgoKUF9DT0xfMTwtZm9yZWNhc3QoQVJJTUExX0NvbG9tYmlhLHhyZWcgPSBUZXN0X0NvbG9tYmlhX1RlbXApCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0NPTF8xPC1SRShQX0NPTF8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db2xvbWJpYV9UZXN0KSkKTUFQRV9DT0xfMTwtTUFQRShQX0NPTF8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db2xvbWJpYV9UZXN0KSkKTVNFX0NPTF8xPC1NU0UoUF9DT0xfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ29sb21iaWFfVGVzdCkpClBGQV9DT0xfMTwtUEZBKFBfQ09MXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0NvbG9tYmlhX1Rlc3QpKQoKRXJyb3JzLkNPTF8xPC1jKFJFX0NPTF8xLE1BUEVfQ09MXzEsUEZBX0NPTF8xLE1TRV9DT0xfMSkKRXJyb3JzLkNPTF8xCmBgYAoKTWFudWFsIE1BUEU6CmBgYHtyfQpkZDwtZGF0YS5mcmFtZShBY3R1YWw9YXMudmVjdG9yKENPVklEX3Nlcmllc19Db2xvbWJpYV9UZXN0KSwKICAgICAgICAgICAgICAgRm9yZT1QX0NPTF8xJG1lYW4KKQpkZDwtZGQgJT4lIG11dGF0ZShBYnNfUGVyX0Vycm9yPWFicyhBY3R1YWwtRm9yZSkvYWJzKEFjdHVhbCkpCm1lYW4oZGQkQWJzX1Blcl9FcnJvcikqMTAwCmBgYAoKKk1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmUqCgpBUklNQSgyLDIsMikKYGBge3J9CiNBdXRvIEFyaW1hIGZvciBDb2xvbWJpYToKQVJJTUEyX0NvbG9tYmlhPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19Db2xvbWJpYV9UcmFpbikKc3VtbWFyeShBUklNQTJfQ29sb21iaWEpCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpQX0NPTF8yPC1mb3JlY2FzdChBUklNQTJfQ29sb21iaWEsaD1sZW5ndGgoQ09WSURfc2VyaWVzX0NvbG9tYmlhX1Rlc3QpKQpgYGAKCkNhbGN1bGF0ZSBlcnJvcnM6CmBgYHtyfQpSRV9DT0xfMjwtUkUoUF9DT0xfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ29sb21iaWFfVGVzdCkpCk1BUEVfQ09MXzI8LU1BUEUoUF9DT0xfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ29sb21iaWFfVGVzdCkpCk1TRV9DT0xfMjwtTVNFKFBfQ09MXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0NvbG9tYmlhX1Rlc3QpKQpQRkFfQ09MXzI8LVBGQShQX0NPTF8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19Db2xvbWJpYV9UZXN0KSkKCkVycm9ycy5DT0xfMjwtYyhSRV9DT0xfMixNQVBFX0NPTF8yLE1TRV9DT0xfMixQRkFfQ09MXzIpCkVycm9ycy5DT0xfMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5DT0xfMSxFcnJvcnMuQ09MXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMiwyLDIpIiwiQVJJTUEoMiwyLDIpIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMiwyLDIpIiwiQVJJTUEoMiwyLDIpIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIENvbG9tYmlhOgoKQVJJTUFfRmluYWxfQ29sb21iaWE8LWF1dG8uYXJpbWEoQ09WSURfRGF5X3Nlcmllc19Db2xvbWJpYV9UZW1wKQpzdW1tYXJ5KEFSSU1BX0ZpbmFsX0NvbG9tYmlhKQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX0NvbG9tYmlhX1RlbXA8LUNvbG9tYmlhX1RlbXAkVGVtcGVyYXR1cmVfQ29sb21iaWFbQ29sb21iaWFfVGVtcCREYXRlPm1heChDT1ZJRF8yJERhdGUyKV0KClBfQ09MX0ZpbmFsPC1mb3JlY2FzdChBUklNQV9GaW5hbF9Db2xvbWJpYSxoPWxlbmd0aChGdXR1cmVfQ29sb21iaWFfVGVtcCkpCkxvd19saW1fQ09MPC1kYXRhLmZyYW1lKFBfQ09MX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9DT0w8LWRhdGEuZnJhbWUoUF9DT0xfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9Db2xvbWJpYV9UZW1wJERhdGUyKSkKcGVyXzIgPC0gc2VxKGFzLkRhdGUobWF4KENPVklEXzJfRGF5X0NvbG9tYmlhX1RlbXAkRGF0ZTIpKzEsZm9ybWF0PSIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDIwLTA0LTMwIixmb3JtYXQ9IiVZLSVtLSVkIiksIjEgZGF5IikKCgojIE1lcmdlIGZvcmVjYXN0ICsgYWN0dWFscwpkYXRhIDwtIHh0cyhDT1ZJRF9EYXlfc2VyaWVzX0NvbG9tYmlhX1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX0NPTCA8LSB4dHMoTG93X2xpbV9DT0wsb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X0NPTCA8LSB4dHMoUF9DT0xfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9DT0wgPC0geHRzKFVwcF9saW1fQ09MLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfQ09MKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fQ09MLCBGb3JlY2FzdF9DT0wsIFVwcF9saW1fQ09MKQoKYWxsX3Nlcmllc19DT0wgPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19DT0wpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19DT0wsIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgQ29sb21iaWEgY2FzZXMiLHhsYWI9IkRhdGUiLCB5bGFiPSJOb3ZlbCBjb3JvbmF2aXJ1cyBjYXNlcyIsd2lkdGggPSA3NTApJT4lCiAgZHlTZXJpZXMoYygnTG93ZXJfbGltaXQnLCAnRm9yZWNhc3QnLCAnVXBwZXJfbGltaXQnKSxsYWJlbD0iRm9yZWNhc3QiLHN0cm9rZVdpZHRoPTIsCiAgICAgICAgICAgZHJhd1BvaW50cyA9IFRSVUUsIHBvaW50U2l6ZSA9IDIsIGNvbG9yPXJnYigxNy8yNTUsNTcvMjU1LDE0MS8yNTUpKSAlPiUKICBkeVNlcmllcygiQWN0dWFsIixkcmF3UG9pbnRzID0gVFJVRSwgc3Ryb2tlV2lkdGg9MiwgcG9pbnRTaXplID0gMiwKICAgICAgICAgICBjb2xvcj1yZ2IoMjQ2LzI1NSwyMDkvMjU1LDc1LzI1NSkpICU+JSAKICBkeVJhbmdlU2VsZWN0b3IoKQoKYGBgCgojIyBDaGlsZQoKIVtdKENoaWxlRmxhZy5wbmcpe3dpZHRoPTQwJX0KCipNb2RlbCB3aXRoIHRlbXBlcmF0dXJlKgoKYGBge3J9CkNoaWxlX1RlbXA8LXJlYWQuY3N2KCJDaGlsZV9UZW1wZXJhdHVyZS5jc3YiLHNlcD0iOyIpCkNoaWxlX1RlbXAkRGF0ZTwtYXMuRGF0ZShDaGlsZV9UZW1wJERhdGUsZm9ybWF0PSIlZC8lbS8lWSIpCmBgYAoKYGBge3J9CkNPVklEXzJfRGF5X0NoaWxlPC0gQ09WSURfMiAlPiUgCiAgZmlsdGVyKENvdW50cnkuUmVnaW9uICVpbiUgYygiQ2hpbGUiKSkgJT4lIAogIGdyb3VwX2J5KERhdGUyKSAlPiUgc3VtbWFyaXNlKFdvcmxkX2NvbmZpcm1lZD1zdW0oQ29uZmlybWVkKSkKCkNPVklEXzJfRGF5X0NoaWxlX1RlbXA8LUNPVklEXzJfRGF5X0NoaWxlICU+JQogIGlubmVyX2pvaW4oQ2hpbGVfVGVtcCxieT1jKCJEYXRlMiI9IkRhdGUiKSkKCkNPVklEX0RheV9zZXJpZXNfQ2hpbGVfVGVtcDwteHRzKENPVklEXzJfRGF5X0NoaWxlX1RlbXAkV29ybGRfY29uZmlybWVkLCBvcmRlci5ieT1DT1ZJRF8yX0RheV9DaGlsZV9UZW1wJERhdGUyKQpgYGAKCmBgYHtyfQpDT1ZJRF9zZXJpZXNfQ2hpbGVfVHJhaW48LUNPVklEX0RheV9zZXJpZXNfQ2hpbGVfVGVtcFsxOjc2XSAjVW50aWwgQXByaWwgNnRoCkNPVklEX3Nlcmllc19DaGlsZV9UZXN0PC1DT1ZJRF9EYXlfc2VyaWVzX0NoaWxlX1RlbXBbNzc6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfQ2hpbGVfVGVtcCldICNGcm9tIEFwcmlsIDd0aCBvbndhcmRzCmBgYAoKR2V0IHRoZSBsYWdnZWQgZGlmZmVyZW5jZToKYGBge3J9CnBsb3QoZGlmZihDT1ZJRF9zZXJpZXNfQ2hpbGVfVHJhaW4pLHR5cGU9ImwiLG1haW49IjFzdCAiKSAKYGBgCgpDb3JyZWxvZ3JhbXMgb2YgZmlyc3QgZGlmZXJlbmNlOgpgYGB7cn0KdHNkaXNwbGF5KGRpZmYoQ09WSURfc2VyaWVzX0NoaWxlX1RyYWluKSkKYGBgCgpBcmltYSBtb2RlbDogQVJJTUEoMSwxLDApCmBgYHtyfQojQXV0byBBcmltYSBmb3IgQ2hpbGU6CgpBUklNQTFfQ2hpbGU8LWF1dG8uYXJpbWEoQ09WSURfc2VyaWVzX0NoaWxlX1RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhyZWcgPSBDT1ZJRF8yX0RheV9DaGlsZV9UZW1wJFRlbXBlcmF0dXJlX0NoaWxlWzE6NzZdKQojQVJJTUExX01leGljbzwtQXJpbWEoQ09WSURfc2VyaWVzX01leGljb19UcmFpbixvcmRlcj1jKDIsMiwzKSx4cmVnPUNPVklEXzJfRGF5X01leGljb19UZW1wJFRlbXBlcmF0dXJlX01leGljb1sxOjc0XSkKc3VtbWFyeShBUklNQTFfQ2hpbGUpCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpUZXN0X0NoaWxlX1RlbXA8LUNPVklEXzJfRGF5X0NoaWxlX1RlbXAkVGVtcGVyYXR1cmVfQ2hpbGVbNzc6bGVuZ3RoKENPVklEX0RheV9zZXJpZXNfQ2hpbGVfVGVtcCldCgoKUF9DSExfMTwtZm9yZWNhc3QoQVJJTUExX0NoaWxlLHhyZWcgPSBUZXN0X0NoaWxlX1RlbXApCmBgYAoKQ2FsY3VsYXRlIGVycm9yczoKYGBge3J9ClJFX0NITF8xPC1SRShQX0NITF8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19DaGlsZV9UZXN0KSkKTUFQRV9DSExfMTwtTUFQRShQX0NITF8xJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19DaGlsZV9UZXN0KSkKTVNFX0NITF8xPC1NU0UoUF9DSExfMSRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ2hpbGVfVGVzdCkpClBGQV9DSExfMTwtUEZBKFBfQ0hMXzEkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0NoaWxlX1Rlc3QpKQoKRXJyb3JzLkNITF8xPC1jKFJFX0NITF8xLE1BUEVfQ0hMXzEsTVNFX0NITF8xLFBGQV9DSExfMSkKRXJyb3JzLkNITF8xCmBgYAoKTWFudWFsIE1BUEU6CmBgYHtyfQpkZDwtZGF0YS5mcmFtZShBY3R1YWw9YXMudmVjdG9yKENPVklEX3Nlcmllc19DaGlsZV9UZXN0KSwKICAgICAgICAgICAgICAgRm9yZT1QX0NITF8xJG1lYW4KKQpkZDwtZGQgJT4lIG11dGF0ZShBYnNfUGVyX0Vycm9yPWFicyhBY3R1YWwtRm9yZSkvYWJzKEFjdHVhbCkpCm1lYW4oZGQkQWJzX1Blcl9FcnJvcikqMTAwCmBgYAoKKk1vZGVsIHdpdGhvdXQgdGVtcGVyYXR1cmUqCgpBUklNQSgxLDIsNCkKYGBge3J9CiNBdXRvIEFyaW1hIGZvciBDb2xvbWJpYToKQVJJTUEyX0NoaWxlPC1hdXRvLmFyaW1hKENPVklEX3Nlcmllc19DaGlsZV9UcmFpbikKc3VtbWFyeShBUklNQTJfQ2hpbGUpCmBgYAoKRm9yZWNhc3QgcHJlZGljdGlvbiB0byBjb21wYXJlCmBgYHtyfQpQX0NITF8yPC1mb3JlY2FzdChBUklNQTJfQ2hpbGUsaD1sZW5ndGgoQ09WSURfc2VyaWVzX0NoaWxlX1Rlc3QpKQpgYGAKCkNhbGN1bGF0ZSBlcnJvcnM6CmBgYHtyfQpSRV9DSExfMjwtUkUoUF9DSExfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ2hpbGVfVGVzdCkpCk1BUEVfQ0hMXzI8LU1BUEUoUF9DSExfMiRtZWFuLGFzLnZlY3RvcihDT1ZJRF9zZXJpZXNfQ2hpbGVfVGVzdCkpCk1TRV9DSExfMjwtTVNFKFBfQ0hMXzIkbWVhbixhcy52ZWN0b3IoQ09WSURfc2VyaWVzX0NoaWxlX1Rlc3QpKQpQRkFfQ0hMXzI8LVBGQShQX0NITF8yJG1lYW4sYXMudmVjdG9yKENPVklEX3Nlcmllc19DaGlsZV9UZXN0KSkKCkVycm9ycy5DSExfMjwtYyhSRV9DSExfMixNQVBFX0NITF8yLE1TRV9DSExfMixQRkFfQ0hMXzIpCkVycm9ycy5DSExfMgpgYGAKCmBgYHtyfQpFcnJvcnM8LXJiaW5kKEVycm9ycy5DSExfMSxFcnJvcnMuQ0hMXzIpCgpyb3duYW1lcyhFcnJvcnMpPC1jKCJBUklNQVgoMSwxLDApIiwiQVJJTUEoMSwyLDQpIikKCmNvbG5hbWVzKEVycm9ycyk8LWMoIlJlbGF0aXZlIGVycm9yIiwiTWVhbiBBYnMgJSBlcnJvciIsIk1lYW4gU3F1YXJlZCBFcnJvciIsIlBGQSIpCgpFcnJvcnM8LWFzLmRhdGEuZnJhbWUoRXJyb3JzKQoKbWF4aW11bTwtYXBwbHkoRXJyb3JzLDIsbWF4KQoKbWluaW11bTwtYXBwbHkoRXJyb3JzLDIsbWluKQoKRXJyb3JzPC1yYmluZChtaW5pbXVtLEVycm9ycykKCkVycm9yczwtcmJpbmQobWF4aW11bSxFcnJvcnMpCmBgYAoKYGBge3J9CnJhZGFyY2hhcnQoRXJyb3JzLG1heG1pbj1UUlVFLGF4aXN0eXBlPTQsYXhpc2xhYmNvbD0ic2xhdGVncmF5NCIsCiAgICAgICAgICAgY2VudGVyemVybz1GQUxTRSxzZWc9OCxjZ2xjb2w9ImdyYXk2NyIsCiAgICAgICAgICAgcGNvbD1jKCJkb2RnZXJibHVlMiIsImZpcmVicmljazIiLCJkYXJrb3JhbmdlMiIsImRhcmtvcmNoaWQyIiksCiAgICAgICAgICAgcGx0eT0xLAogICAgICAgICAgIHBsd2Q9MywKICAgICAgICAgICB0aXRsZT0iRXJyb3IgY29tcGFyaXNvbiIpCgpsZWdlbmQgPC1sZWdlbmQoMS41LDEsIGxlZ2VuZD1jKCJBUklNQVgoMSwxLDApIiwiQVJJTUEoMSwyLDQpIiksCiAgICAgICAgICAgICAgICAgc2VnLmxlbj0tMS40LAogICAgICAgICAgICAgICAgIHRpdGxlPSJFcnJvcnMiLAogICAgICAgICAgICAgICAgIHBjaD0yMSwgCiAgICAgICAgICAgICAgICAgYnR5PSJuIiAsbHdkPTMsIHkuaW50ZXJzcD0xLCBob3Jpej1GQUxTRSwKICAgICAgICAgICAgICAgICBjb2w9YygiZG9kZ2VyYmx1ZTIiLCJmaXJlYnJpY2syIiwiZGFya29yYW5nZTIiLCJkYXJrb3JjaGlkMiIpKQpgYGAKCkNvbmNsdXNpb246IEtlZXAgbW9kZWwgd2l0aG91dCB0ZW1wZXJhdHVyZQoKTWFrZSBtb2RlbCB3aXRoIHRoZSBvdmVyYWxsIHNlcmllcwpgYGB7cn0KI0F1dG8gQXJpbWEgZm9yIENoaWxlOgoKQVJJTUFfRmluYWxfQ2hpbGU8LWF1dG8uYXJpbWEoQ09WSURfRGF5X3Nlcmllc19DaGlsZV9UZW1wKQpzdW1tYXJ5KEFSSU1BX0ZpbmFsX0NoaWxlKQpgYGAKCkZpbmFsIGZvcmVjYXN0OgpgYGB7cn0KRnV0dXJlX0NoaWxlX1RlbXA8LUNoaWxlX1RlbXAkVGVtcGVyYXR1cmVfQ2hpbGVbQ2hpbGVfVGVtcCREYXRlPm1heChDT1ZJRF8yJERhdGUyKV0KClBfQ0hMX0ZpbmFsPC1mb3JlY2FzdChBUklNQV9GaW5hbF9DaGlsZSxoPWxlbmd0aChGdXR1cmVfQ2hpbGVfVGVtcCkpCkxvd19saW1fQ0hMPC1kYXRhLmZyYW1lKFBfQ0hMX0ZpbmFsJGxvd2VyKVssMl0KVXBwX2xpbV9DSEw8LWRhdGEuZnJhbWUoUF9DSExfRmluYWwkdXBwZXIpWywyXQpgYGAKCkZvciBtYWtpbmcgdGhlIHBsb3Q6CmBgYHtyfQojI0RhdGEgcGVyaW9kcwpwZXJfMSA8LSBhcy5EYXRlKGFzLmNoYXJhY3RlcihDT1ZJRF8yX0RheV9DaGlsZV9UZW1wJERhdGUyKSkKcGVyXzIgPC0gc2VxKGFzLkRhdGUobWF4KENPVklEXzJfRGF5X0NoaWxlX1RlbXAkRGF0ZTIpKzEsZm9ybWF0PSIlWS0lbS0lZCIpLCBhcy5EYXRlKCIyMDIwLTA0LTMwIixmb3JtYXQ9IiVZLSVtLSVkIiksIjEgZGF5IikKCgojIE1lcmdlIGZvcmVjYXN0ICsgYWN0dWFscwpkYXRhIDwtIHh0cyhDT1ZJRF9EYXlfc2VyaWVzX0NoaWxlX1RlbXAsb3JkZXIuYnk9cGVyXzEpIApkYXRhTkEgPC0gcmVwKE5BLCBsZW5ndGgoZGF0YSkpCkEgPC0gY2JpbmQoZGF0YSxkYXRhTkEsZGF0YU5BLGRhdGFOQSkKCgpMb3dfbGltX0NITCA8LSB4dHMoTG93X2xpbV9DSEwsb3JkZXIuYnk9cGVyXzIpCkZvcmVjYXN0X0NITCA8LSB4dHMoUF9DSExfRmluYWwkbWVhbixvcmRlci5ieT1wZXJfMikKVXBwX2xpbV9DSEwgPC0geHRzKFVwcF9saW1fQ0hMLG9yZGVyLmJ5PXBlcl8yKQpwcmVkTkEgPC0gcmVwKE5BLCBsZW5ndGgoRm9yZWNhc3RfQ0hMKSkKQiA8LSBjYmluZChwcmVkTkEsIExvd19saW1fQ0hMLCBGb3JlY2FzdF9DSEwsIFVwcF9saW1fQ0hMKQoKYWxsX3Nlcmllc19DSEwgPC0gZGF0YS5mcmFtZShyYmluZChhcy5tYXRyaXgoQSksYXMubWF0cml4KEIpKSkKY29sbmFtZXMoYWxsX3Nlcmllc19DSEwpIDwtIGMoJ0FjdHVhbCcsICdMb3dlcl9saW1pdCcsICdGb3JlY2FzdCcsICdVcHBlcl9saW1pdCcpCmBgYAoKYGBge3J9CmR5Z3JhcGgoYWxsX3Nlcmllc19DSEwsIG1haW49IlNBUlMtQ09WMi1vdXRicmVhazogVG90YWwgQ2hpbGUgY2FzZXMiLHhsYWI9IkRhdGUiLCB5bGFiPSJOb3ZlbCBjb3JvbmF2aXJ1cyBjYXNlcyIsd2lkdGggPSA3NTApJT4lCiAgZHlTZXJpZXMoYygnTG93ZXJfbGltaXQnLCAnRm9yZWNhc3QnLCAnVXBwZXJfbGltaXQnKSxsYWJlbD0iRm9yZWNhc3QiLHN0cm9rZVdpZHRoPTIsCiAgICAgICAgICAgZHJhd1BvaW50cyA9IFRSVUUsIHBvaW50U2l6ZSA9IDIsIGNvbG9yPXJnYigxOTYvMjU1LDYwLzI1NSw0NC8yNTUpKSAlPiUKICBkeVNlcmllcygiQWN0dWFsIixkcmF3UG9pbnRzID0gVFJVRSwgc3Ryb2tlV2lkdGg9MiwgcG9pbnRTaXplID0gMiwKICAgICAgICAgICBjb2xvcj1yZ2IoMTYvMjU1LDU5LzI1NSwxNjAvMjU1KSkgJT4lIAogIGR5UmFuZ2VTZWxlY3RvcigpCgpgYGAKCgoKYGBge3J9CiNGb3JlY2FzdHM8LWxpc3QoYWxsX3Nlcmllc19DUkksYWxsX3Nlcmllc19NRVgsYWxsX3Nlcmllc19JVEEsYWxsX3Nlcmllc19MQk4sYWxsX3Nlcmllc19DT0wsCiAgICAgICAgICAgICAgICAjYWxsX3Nlcmllc19DSEwpCiNuYW1lcyhGb3JlY2FzdHMpPC1jKCJDb3N0YSBSaWNhIiwiTWV4aWNvIiwiSXRhbHkiLCJMZWJhbm9uIiwiQ29sb21iaWEiLCJDaGlsZSIpCiNGb3JlY2FzdHMkTWV4aWNvCgojc2F2ZShGb3JlY2FzdHMsIGZpbGU9IkZvcmVjYXN0cy5SRGF0YSIpCmBgYAoKCg==